OSCAR-code/daily.cpp
2011-08-08 01:37:01 +10:00

1111 lines
41 KiB
C++

/*
Daily Panel
Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
License: GPL
*/
#include "daily.h"
#include "ui_daily.h"
#include <QTextCharFormat>
#include <QTextBlock>
#include <QColorDialog>
#include <QSpacerItem>
#include <QBuffer>
#include <QPixmap>
#include <QMessageBox>
#include <QResizeEvent>
#include "SleepLib/session.h"
#include "Graphs/graphdata_custom.h"
#include "Graphs/gLineOverlay.h"
#include "Graphs/gFlagsLine.h"
#include "Graphs/gFooBar.h"
#include "Graphs/gXAxis.h"
#include "Graphs/gYAxis.h"
#include "Graphs/gBarChart.h"
#include "Graphs/gSegmentChart.h"
const int min_height=150;
const int default_height=150;
MyScrollArea::MyScrollArea(QWidget *parent, Daily * daily)
:QScrollArea(parent),m_daily(daily)
{
timer=new QTimer(this);
m_time.start();
}
MyScrollArea::~MyScrollArea()
{
delete timer;
}
void MyScrollArea::scrollContentsBy(int dx, int dy)
{
QScrollArea::scrollContentsBy(dx,dy);
#ifdef Q_WS_MAC
if (timer->isActive()) timer->stop();
timer->setSingleShot(true);
timer->setInterval(250);
connect(timer,SIGNAL(timeout()),SLOT(UpdateGraphs()));
timer->start();
m_time.start();
//m_daily->RedrawGraphs();
#endif
}
void MyScrollArea::UpdateGraphs()
{
if (m_time.elapsed()<250)
return;
m_time.start();
qDebug() << "Redraw!";
m_daily->RedrawGraphs();
}
Daily::Daily(QWidget *parent,QGLWidget * shared, MainWindow *mw)
:QWidget(parent),mainwin(mw), ui(new Ui::Daily)
{
ui->setupUi(this);
QString prof=pref["Profile"].toString();
profile=Profiles::Get(prof);
if (!profile) {
QMessageBox::critical(this,"Profile Error",QString("Couldn't get profile '%1'.. Have to abort!").arg(pref["Profile"].toString()));
exit(-1);
}
scrollArea=new MyScrollArea(ui->graphMainArea,this);
ui->graphLayout->addWidget(scrollArea,1);
ui->graphLayout->setSpacing(0);
ui->graphLayout->setMargin(0);
ui->graphLayout->setContentsMargins(0,0,0,0);
scrollArea->setWidgetResizable(true);
scrollArea->setAutoFillBackground(false);
GraphLayout=new QWidget(scrollArea);
GraphLayout->setAutoFillBackground(false);
scrollArea->setWidget(GraphLayout);
splitter=new QVBoxLayout(GraphLayout);
GraphLayout->setLayout(splitter);
splitter->setSpacing(0);
splitter->setMargin(0);
splitter->setContentsMargins(0,0,0,0);
//ui->splitter->layout();
//gSplitter(Qt::Vertical,ui->scrollArea);
//splitter->setStyleSheet("QSplitter::handle { background-color: 'light grey'; }");
//splitter->setHandleWidth(3);
#ifdef Q_WS_MAC
//splitter->setOpaqueResize(false);
#endif
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//ui->scrollArea->setWidget(splitter);
//this->connect(ui->scrollArea,
//ui->graphSizer->addWidget(splitter);
scrollArea->setAutoFillBackground(false);
//splitter->setAutoFillBackground(false);
//splitter->setMinimumHeight(1600);
//splitter->setMinimumWidth(600);
QWidget * parental=GraphLayout;
SF=new gGraphWindow(parental,tr("Event Flags"),shared);
FRW=new gGraphWindow(parental,tr("Flow Rate"),SF);
PRD=new gGraphWindow(parental,tr("Pressure"),SF);
//EXPR=new gGraphWindow(parental,tr("Exp. Pressure"),SF);
THPR=new gGraphWindow(parental,tr("Therapy Pressure"),SF);
LEAK=new gGraphWindow(parental,tr("Leaks"),SF);
MP=new gGraphWindow(parental,tr("Mask Pressure"),SF);
SNORE=new gGraphWindow(parental,tr("Snore"),SF);
FLG=new gGraphWindow(parental,tr("Flow Limitation"),SF);
MV=new gGraphWindow(parental,tr("Minute Ventilation"),SF);
TV=new gGraphWindow(parental,tr("Tidal Volume"),SF);
RR=new gGraphWindow(parental,tr("Respiratory Rate"),SF);
PTB=new gGraphWindow(parental,tr("Patient Trig Breaths"),SF);
//OF=new gGraphWindow(parental,tr("Oxi-Flags"),SF);
INTPULSE=new gGraphWindow(parental,tr("Pulse"),SF); // Integrated Pulse
INTSPO2=new gGraphWindow(parental,tr("SPO2"),SF); // Integrated Pulse
PULSE=new gGraphWindow(parental,tr("Pulse"),SF);
SPO2=new gGraphWindow(parental,tr("SPO2"),SF);
PLETHY=new gGraphWindow(parental,tr("Plethysomogram"),SF);
TAP=new gGraphWindow(NULL,"",(QGLWidget* )NULL);
TAP_EAP=new gGraphWindow(NULL,"",(QGLWidget* )NULL);
TAP_IAP=new gGraphWindow(NULL,"",(QGLWidget* )NULL);
G_AHI=new gGraphWindow(NULL,"",(QGLWidget* )NULL);
/*QGLFormat fmt;
fmt.setDepth(false);
fmt.setDirectRendering(false);
fmt.setAlpha(true);
fmt.setDoubleBuffer(false);
fmt.setRgba(true);
//fmt.setDefaultFormat(fmt);
offscreen_context=new QGLContext(fmt); */
SF->SetLeftMargin(SF->GetLeftMargin()+gYAxis::Margin);
SF->SetBlockZoom(true);
SF->AddLayer(new gXAxis());
SF->setMinimumHeight(min_height);
fg=new gFlagsGroup();
fg->AddLayer((new gFlagsLine(CPAP_CSR,QColor("light green"),"CSR",false,FT_Span)));
fg->AddLayer((new gFlagsLine(CPAP_ClearAirway,QColor("purple"),"CA",true)));
fg->AddLayer((new gFlagsLine(CPAP_Obstructive,QColor("#40c0ff"),"OA",true)));
fg->AddLayer((new gFlagsLine(CPAP_Apnea,QColor("dark green"),"A")));
fg->AddLayer((new gFlagsLine(CPAP_Hypopnea,QColor("blue"),"H",true)));
fg->AddLayer((new gFlagsLine(CPAP_FlowLimit,QColor("black"),"FL")));
fg->AddLayer((new gFlagsLine(CPAP_RERA,QColor("gold"),"RE")));
fg->AddLayer((new gFlagsLine(CPAP_VSnore,QColor("red"),"VS")));
//fg->AddLayer(AddCPAP(new gFlagsLine(flags[8],QColor("dark green"),"U0E")));
//fg->AddLayer(AddCPAP(new gFlagsLine(flags[10],QColor("red"),"VS2"));
SF->AddLayer(AddCPAP(fg));
// SF Foobar must go last
SF->AddLayer(new gFooBar(10,QColor("orange"),QColor("dark grey"),true));
bool square=true;
PRD->AddLayer(new gXAxis());
PRD->AddLayer(new gYAxis());
PRD->AddLayer(AddCPAP(pressure=new gLineChart(CPAP_Pressure,QColor("dark green"),square)));
PRD->AddLayer(AddCPAP(epap=new gLineChart(CPAP_EPAP,Qt::blue,square)));
PRD->AddLayer(AddCPAP(ipap=new gLineChart(CPAP_IPAP,Qt::red,square)));
PRD->setMinimumHeight(min_height);
THPR->AddLayer(new gXAxis());
THPR->AddLayer(new gYAxis());
THPR->AddLayer(AddCPAP(new gLineChart(CPAP_TherapyPressure,QColor("dark green"),square)));
THPR->AddLayer(AddCPAP(new gLineChart(CPAP_ExpiratoryPressure,QColor("dark blue"),square)));
THPR->setMinimumHeight(min_height);
//EXPR->AddLayer(new gXAxis());
//EXPR->AddLayer(new gYAxis());
//EXPR->setMinimumHeight(min_height);
LEAK->AddLayer(new gXAxis());
LEAK->AddLayer(new gYAxis());
//LEAK->AddLayer(new gFooBar());
LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_Leak,QColor("purple"),true)));
LEAK->setMinimumHeight(min_height);
MP->AddLayer(new gYAxis());
MP->AddLayer(new gXAxis());
gLineChart *g=new gLineChart(CPAP_MaskPressure,Qt::blue,false);
AddCPAP(g);
g->ReportEmpty(true);
MP->AddLayer(g);
//MP->AddLayer(AddCPAP(new gLineChart(CPAP_EPAP,Qt::yellow,square)));
//MP->AddLayer(AddCPAP(new gLineChart(CPAP_IPAP,Qt::red,square)));
MP->setMinimumHeight(min_height);
//FRW->AddLayer(new gFooBar());
FRW->AddLayer(new gYAxis());
FRW->AddLayer(new gXAxis());
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_CSR,QColor("light green"),"CSR",FT_Span)));
g=new gLineChart(CPAP_FlowRate,Qt::black,false);
g->ReportEmpty(true);
AddCPAP(g);
FRW->AddLayer(g);
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_Hypopnea,QColor("blue"),"H")));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_PressurePulse,QColor("red"),"PR",FT_Dot)));
//FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_Pressure,QColor("white"),"P",FT_Dot)));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(PRS1_Unknown0B,QColor("blue"),"0B",FT_Dot)));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(PRS1_Unknown10,QColor("orange"),"10",FT_Dot)));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(PRS1_Unknown0E,QColor("yellow"),"0E",FT_Dot)));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_RERA,QColor("gold"),"RE")));
//FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_Unknown0E,QColor("dark green"),"U0E")));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_Apnea,QColor("dark green"),"A")));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_VSnore,QColor("red"),"VS")));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_FlowLimit,QColor("black"),"FL")));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_Obstructive,QColor("#40c0ff"),"OA")));
FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_ClearAirway,QColor("purple"),"CA")));
FRW->setMinimumHeight(min_height);
SNORE->AddLayer(new gXAxis());
SNORE->AddLayer(new gYAxis());
SNORE->AddLayer(AddCPAP(new gLineChart(CPAP_Snore,Qt::black,true)));
SNORE->setMinimumHeight(min_height);
FLG->AddLayer(new gXAxis());
FLG->AddLayer(new gYAxis());
FLG->AddLayer(AddCPAP(new gLineChart(CPAP_FlowLimitGraph,Qt::black,true)));
FLG->setMinimumHeight(min_height);
MV->AddLayer(new gXAxis());
MV->AddLayer(new gYAxis());
MV->AddLayer(AddCPAP(new gLineChart(CPAP_MinuteVentilation,QColor(0x20,0x20,0x7f),true)));
MV->setMinimumHeight(min_height);
TV->AddLayer(new gXAxis());
TV->AddLayer(new gYAxis());
TV->AddLayer(AddCPAP(new gLineChart(CPAP_TidalVolume,QColor(0x7f,0x20,0x20),true)));
TV->setMinimumHeight(min_height);
RR->AddLayer(new gXAxis());
RR->AddLayer(new gYAxis());
RR->AddLayer(AddCPAP(new gLineChart(CPAP_RespiratoryRate,Qt::gray,true)));
RR->setMinimumHeight(min_height);
PTB->AddLayer(new gXAxis());
PTB->AddLayer(new gYAxis());
PTB->AddLayer(AddCPAP(new gLineChart(CPAP_PatientTriggeredBreaths,Qt::gray,true)));
PTB->setMinimumHeight(min_height);
INTPULSE->AddLayer(new gXAxis());
INTPULSE->AddLayer(new gYAxis());
INTPULSE->AddLayer(AddCPAP(new gLineChart(CPAP_Pulse,Qt::red,true)));
INTPULSE->setMinimumHeight(min_height);
INTSPO2->AddLayer(new gXAxis());
INTSPO2->AddLayer(new gYAxis());
INTSPO2->AddLayer(AddCPAP(new gLineChart(CPAP_SPO2,Qt::blue,true)));
INTSPO2->setMinimumHeight(min_height);
PLETHY->AddLayer(new gXAxis());
PLETHY->AddLayer(new gYAxis());
PLETHY->AddLayer(AddOXI(new gLineChart(OXI_Plethysomogram,Qt::darkCyan,true)));
PLETHY->setMinimumHeight(min_height);
PULSE->AddLayer(new gXAxis());
PULSE->AddLayer(new gYAxis());
PULSE->AddLayer(AddOXI(new gLineChart(OXI_Pulse,Qt::red,true)));
PULSE->AddLayer(AddOXI(new gLineOverlayBar(OXI_PulseChange,Qt::green,"PC",FT_Bar)));
PULSE->setMinimumHeight(min_height);
SPO2->AddLayer(new gXAxis());
SPO2->AddLayer(new gYAxis());
SPO2->AddLayer(AddOXI(new gLineChart(OXI_SPO2,Qt::blue,true)));
SPO2->AddLayer(AddOXI(new gLineOverlayBar(OXI_SPO2Drop,Qt::red,"SC",FT_Bar)));
SPO2->setMinimumHeight(min_height);
/*fg=new gFlagsGroup();
fg->AddLayer(AddOXI(new gFlagsLine(OXI_PulseChange,QColor("orange"),"PUL",true)));
fg->AddLayer(AddOXI(new gFlagsLine(OXI_SPO2Change,QColor("gray"),"SP",true)));
OF->AddLayer(fg);
OF->AddLayer(new gFooBar(10,QColor("orange"),QColor("dark grey"),true));
OF->SetLeftMargin(SF->GetLeftMargin()+gYAxis::Margin);
OF->SetBlockZoom(true);
OF->AddLayer(new gXAxis());
OF->setMinimumHeight(min_height); */
// SPO2->AddLayer(new gFooBar());
// SPO2->setMinimumHeight(min_height);
// SPO2->LinkZoom(PULSE);
// PULSE->LinkZoom(SPO2);
// SPO2->hide();
// PULSE->hide();
gSegmentChart *seg;
TAP_EAP->SetMargins(0,0,0,0);
TAP_EAP->AddLayer(AddCPAP(seg=new gTAPGraph(CPAP_EPAP)));
TAP_EAP->hide();
TAP_EAP->SetGradientBackground(false);
TAP_IAP->SetMargins(0,0,0,0);
TAP_IAP->AddLayer(AddCPAP(seg=new gTAPGraph(CPAP_IPAP)));
TAP_IAP->hide();
TAP_IAP->SetGradientBackground(false);
TAP->SetMargins(0,0,0,0);
TAP->AddLayer(AddCPAP(seg=new gTAPGraph(CPAP_Pressure,GST_CandleStick)));
TAP->hide();
TAP->SetGradientBackground(false);
G_AHI->SetMargins(0,0,0,0);
seg=new gSegmentChart(GST_Pie);
seg->AddSlice(CPAP_Hypopnea,QColor(0x40,0x40,0xff,0xff),"H");
seg->AddSlice(CPAP_Apnea,QColor(0x20,0x80,0x20,0xff),"A");
seg->AddSlice(CPAP_Obstructive,QColor(0x40,0xaf,0xbf,0xff),"OA");
seg->AddSlice(CPAP_ClearAirway,QColor(0xb2,0x54,0xcd,0xff),"CA");
seg->AddSlice(CPAP_RERA,QColor(0xff,0xff,0x80,0xff),"RE");
seg->AddSlice(CPAP_FlowLimit,QColor(0x40,0x40,0x40,0xff),"FL");
G_AHI->AddLayer(AddCPAP(seg));
G_AHI->SetGradientBackground(false);
G_AHI->hide();
NoData=new QLabel(tr("No data"),parental);
NoData->setAlignment(Qt::AlignCenter);
QFont font("FreeSans",20); //NoData->font();
//font.setBold(true);
NoData->setFont(font);
NoData->hide();
splitter->addWidget(NoData);
//int i=splitter->indexOf(NoData);
splitter->setStretchFactor(NoData,1);
gGraphWindow * graphs[]={SF,FRW,MP,MV,TV,PTB,RR,PRD,THPR,LEAK,FLG,SNORE,INTPULSE,INTSPO2};
int ss=sizeof(graphs)/sizeof(gGraphWindow *);
for (int i=0;i<ss;i++) {
AddGraph(graphs[i]);
//int j=splitter->indexOf(graphs[i]);
splitter->setStretchFactor(graphs[i],1);
//splitter->setAlignment(graphs[i],Qt::AlignTop);
for (int j=0;j<ss;j++) {
if (graphs[i]!=graphs[j]) {
graphs[i]->LinkZoom(graphs[j]);
}
}
}
//AddGraph(OF);
AddGraph(PULSE);
AddGraph(SPO2);
AddGraph(PLETHY);
//SPO2->LinkZoom(OF);
//PULSE->LinkZoom(OF);
SPO2->LinkZoom(PULSE);
SPO2->LinkZoom(PLETHY);
PULSE->LinkZoom(SPO2);
PULSE->LinkZoom(PLETHY);
PLETHY->LinkZoom(PULSE);
PLETHY->LinkZoom(SPO2);
//OF->LinkZoom(PULSE);
//OF->LinkZoom(SPO2);
// AddGraph(SPO2);
spacer=new gGraphWindow(scrollArea,"",SF);
spacer->setMinimumHeight(1);
spacer->setMaximumHeight(1);
splitter->addWidget(spacer);
//i=splitter->indexOf(spacer);
//splitter->setStretchFactor(i,1);
//i=splitter->indexOf(FRW);
//splitter->setStretchFactor(i,15);
//splitter->refresh();
//splitter->setChildrenCollapsible(false); // We set this per widget..
//splitter->setCollapsible(splitter->indexOf(SF),false);
//splitter->setStretchFactor(splitter->indexOf(SF),0);
//splitter_sizes=splitter->sizes();
//splitter->layout();
//splitter->update();
QTextCharFormat format = ui->calendar->weekdayTextFormat(Qt::Saturday);
format.setForeground(QBrush(Qt::black, Qt::SolidPattern));
ui->calendar->setWeekdayTextFormat(Qt::Saturday, format);
ui->calendar->setWeekdayTextFormat(Qt::Sunday, format);
ui->tabWidget->setCurrentWidget(ui->details);
if (mainwin) {
show_graph_menu=mainwin->CreateMenu("Graphs");
show_graph_menu->clear();
for (int i=0;i<Graphs.size();i++) {
QAction * action=show_graph_menu->addAction(Graphs[i]->Title(),NULL,NULL,0);
action->setCheckable(true);
action->setChecked(true);
connect(action, SIGNAL(triggered()), this, SLOT(ShowHideGraphs()));
GraphAction.push_back(action);
}
} else show_graph_menu=NULL;
}
Daily::~Daily()
{
// Save any last minute changes..
if (previous_date.isValid())
Unload(previous_date);
// delete splitter;
delete ui;
}
void Daily::AddGraph(gGraphWindow *w)
{
Graphs.push_back(w);
splitter->addWidget(w);
w->SetSplitter(splitter);
}
void Daily::resizeEvent (QResizeEvent * event)
{
//const QSize &size=event->size();
// splitter->setMinimumWidth(size.width()-280);
}
void Daily::ReloadGraphs()
{
QDate d=profile->LastDay();
if (!d.isValid()) {
d=ui->calendar->selectedDate();
}
on_calendar_currentPageChanged(d.year(),d.month());
ui->calendar->setSelectedDate(d);
Load(d);
}
void Daily::on_calendar_currentPageChanged(int year, int month)
{
QDate d(year,month,1);
int dom=d.daysInMonth();
for (int i=1;i<=dom;i++) {
d=QDate(year,month,i);
this->UpdateCalendarDay(d);
}
}
void Daily::UpdateEventsTree(QTreeWidget *tree,Day *day)
{
tree->clear();
if (!day) return;
//return;
tree->setColumnCount(1); // 1 visible common.. (1 hidden)
QTreeWidgetItem *root=NULL;//new QTreeWidgetItem((QTreeWidget *)0,QStringList("Stuff"));
QHash<ChannelID,QTreeWidgetItem *> mcroot;
QHash<ChannelID,int> mccnt;
int total_events=0;
for (QVector<Session *>::iterator s=day->begin();s!=day->end();s++) {
QHash<ChannelID,QVector<EventList *> >::iterator m;
//QTreeWidgetItem * sroot;
for (m=(*s)->eventlist.begin();m!=(*s)->eventlist.end();m++) {
ChannelID code=m.key();
if ((code!=CPAP_Obstructive)
&& (code!=CPAP_Hypopnea)
&& (code!=CPAP_Apnea)
&& (code!=PRS1_Unknown0B)
&& (code!=CPAP_ClearAirway)
&& (code!=CPAP_CSR)
&& (code!=CPAP_RERA)
&& (code!=CPAP_FlowLimit)
&& (code!=CPAP_PressurePulse)
&& (code!=CPAP_VSnore)) continue;
QTreeWidgetItem *mcr;
if (mcroot.find(code)==mcroot.end()) {
int cnt=day->count(code);
total_events+=cnt;
QString st=channel[m.key()].details();
if (st.isEmpty()) {
st="Fixme "+QString::number((int)code);
}
st+=" ("+QString::number(cnt)+" event"+((cnt>1)?"s":"")+")";
QStringList l(st);
l.append("");
mcroot[code]=mcr=new QTreeWidgetItem(root,l);
mccnt[code]=0;
} else {
mcr=mcroot[code];
}
for (int z=0;z<m.value().size();z++) {
for (int o=0;o<m.value()[z]->count();o++) {
qint64 t=m.value()[z]->time(o);
if (code==CPAP_CSR) {
t-=float(m.value()[z]->raw(o)/2.0)*1000.0;
}
QStringList a;
QDateTime d=QDateTime::fromMSecsSinceEpoch(t);
QString s=QString("#%1: %2 (%3)").arg((int)++mccnt[code],(int)3,(int)10,QChar('0')).arg(d.toString("HH:mm:ss")).arg(m.value()[z]->raw(o));
a.append(s);
a.append(d.toString("yyyy-MM-dd HH:mm:ss"));
mcr->addChild(new QTreeWidgetItem(a));
}
}
}
}
int cnt=0;
for (QHash<ChannelID,QTreeWidgetItem *>::iterator m=mcroot.begin();m!=mcroot.end();m++) {
tree->insertTopLevelItem(cnt++,m.value());
}
//tree->insertTopLevelItem(cnt++,new QTreeWidgetItem(QStringList("[Total Events ("+QString::number(total_events)+")]")));
tree->sortByColumn(0,Qt::AscendingOrder);
//tree->expandAll();
}
void Daily::UpdateCalendarDay(QDate date)
{
QTextCharFormat bold;
QTextCharFormat cpapcol;
QTextCharFormat normal;
QTextCharFormat oxiday;
bold.setFontWeight(QFont::Bold);
cpapcol.setForeground(QBrush(Qt::blue, Qt::SolidPattern));
cpapcol.setFontWeight(QFont::Bold);
oxiday.setForeground(QBrush(Qt::red, Qt::SolidPattern));
oxiday.setFontWeight(QFont::Bold);
bool hascpap=profile->GetDay(date,MT_CPAP)!=NULL;
bool hasoxi=profile->GetDay(date,MT_OXIMETER)!=NULL;
if (hascpap) {
if (hasoxi) {
ui->calendar->setDateTextFormat(date,oxiday);
} else {
ui->calendar->setDateTextFormat(date,cpapcol);
}
} else if (profile->GetDay(date)) {
ui->calendar->setDateTextFormat(date,bold);
} else {
ui->calendar->setDateTextFormat(date,normal);
}
ui->calendar->setHorizontalHeaderFormat(QCalendarWidget::ShortDayNames);
}
void Daily::on_calendar_selectionChanged()
{
if (previous_date.isValid())
Unload(previous_date);
Load(ui->calendar->selectedDate());
}
void Daily::ResetGraphLayout()
{
//splitter->setSizes(splitter_sizes);
}
void Daily::ShowHideGraphs()
{
int vis=0;
for (int i=0;i<Graphs.size();i++) {
if (Graphs[i]->isEmpty()) {
GraphAction[i]->setVisible(false);
Graphs[i]->hide();
} else {
Graphs[i]->ResetBounds();
GraphAction[i]->setVisible(true);
if (GraphAction[i]->isChecked()) {
Graphs[i]->show();
vis++;
} else {
Graphs[i]->hide();
}
}
}
GraphLayout->setMinimumHeight(vis*default_height+10);
//splitter->setMaximumHeight(vis*default_height);
splitter->layout();
//splitter->update();
RedrawGraphs();
}
void Daily::Load(QDate date)
{
static Day * lastcpapday=NULL;
previous_date=date;
Day *cpap=profile->GetDay(date,MT_CPAP);
Day *oxi=profile->GetDay(date,MT_OXIMETER);
// Day *sleepstage=profile->GetDay(date,MT_SLEEPSTAGE);
if (!pref["MemoryHog"].toBool()) {
if (lastcpapday && (lastcpapday!=cpap)) {
for (QVector<Session *>::iterator s=lastcpapday->begin();s!=lastcpapday->end();s++) {
(*s)->TrashEvents();
}
}
}
lastcpapday=cpap;
QString html="<html><head><style type='text/css'>p,a,td,body { font-family: 'FreeSans', 'Sans Serif'; } p,a,td,body { font-size: 12px; } </style>"
"</head>"
"<body leftmargin=0 rightmargin=0 topmargin=0 marginwidth=0 marginheight=0>"
"<table cellspacing=0 cellpadding=2 border=0 width='100%'>\n";
QString tmp;
const int gwwidth=240;
const int gwheight=100;
UpdateOXIGraphs(oxi);
UpdateCPAPGraphs(cpap);
UpdateEventsTree(ui->treeWidget,cpap);
if (!cpap && !oxi) {
GraphLayout->setMinimumHeight(0);
//splitter->setMinimumHeight(0);
NoData->setText(tr("No data for ")+date.toString(Qt::SystemLocaleLongDate));
NoData->show();
for (int i=0;i<Graphs.size();i++) {
GraphAction[i]->setVisible(false);
Graphs[i]->hide();
}
spacer->hide();
} else {
NoData->hide();
int vis=0;
scrollArea->setUpdatesEnabled(false);
for (int i=0;i<Graphs.size();i++) {
Graphs[i]->setUpdatesEnabled(false);
if (Graphs[i]->isEmpty()) {
GraphAction[i]->setVisible(false);
Graphs[i]->hide();
} else {
GraphAction[i]->setVisible(true);
if (GraphAction[i]->isChecked()) {
Graphs[i]->ResetBounds();
Graphs[i]->show();
vis++;
} else {
Graphs[i]->hide();
}
}
}
if (!cpap) {
GraphAction[0]->setVisible(false);
SF->hide();
vis--;
}
//splitter->layout();
for (int i=0;i<Graphs.size();i++) {
Graphs[i]->setUpdatesEnabled(true);
}
spacer->show();
//splitter->addStrut(vis*default_height);
//QSize aa(this->width(),vis*default_height);
//splitter->SetMinimumSize(aa);
//splitter->SetFixedSize(aa);
GraphLayout->setMinimumHeight(vis*default_height+20);
//splitter->setMinimumHeight(0);
//splitter->setMinimumHeight(vis*default_height);
//if (vis>4) {
//splitter->setMaximumHeight(vis*default_height);
//} //else {
// }
// splitter->blockSignals(true);
splitter->setSpacing(0);
splitter->setMargin(0);
splitter->layout();
scrollArea->setUpdatesEnabled(true);
scrollArea->update();
//ui->scrollArea->update();
//splitter->layout();
// spacer->show();
}
//splitter->update();
RedrawGraphs();
QString epr,modestr;
float iap90,eap90;
CPAPMode mode=MODE_UNKNOWN;
PRTypes pr;
QString a;
if (cpap) {
mode=(CPAPMode)cpap->settings_max(CPAP_Mode);
pr=(PRTypes)cpap->settings_max(PRS1_PressureReliefType);
if (pr==PR_NONE)
epr=tr(" No Pressure Relief");
else {
epr=channel[PRS1_PressureReliefSetting].optionString(pr)+QString(" x%1").arg((int)cpap->settings_max(PRS1_PressureReliefSetting));
}
modestr=channel[CPAP_Mode].optionString(mode);
float ahi=(cpap->count(CPAP_Obstructive)+cpap->count(CPAP_Hypopnea)+cpap->count(CPAP_ClearAirway)+cpap->count(CPAP_Apnea))/cpap->hours();
float csr=(100.0/cpap->hours())*(cpap->sum(CPAP_CSR)/3600.0);
float uai=cpap->count(CPAP_Apnea)/cpap->hours();
float oai=cpap->count(CPAP_Obstructive)/cpap->hours();
float hi=cpap->count(CPAP_Hypopnea)/cpap->hours();
float cai=cpap->count(CPAP_ClearAirway)/cpap->hours();
float rei=cpap->count(CPAP_RERA)/cpap->hours();
float vsi=cpap->count(CPAP_VSnore)/cpap->hours();
float fli=cpap->count(CPAP_FlowLimit)/cpap->hours();
float p90=cpap->p90(CPAP_Pressure);
eap90=cpap->p90(CPAP_EPAP);
iap90=cpap->p90(CPAP_IPAP);
QString submodel=tr("Unknown Model");
//html+="<tr><td colspan=4 align=center><i>"+tr("Machine Information")+"</i></td></tr>\n";
if (cpap->machine->properties.find("SubModel")!=cpap->machine->properties.end())
submodel=" <br>"+cpap->machine->properties["SubModel"];
html+="<tr><td colspan=4 align=center><b>"+cpap->machine->properties["Brand"]+"</b> <br>"+cpap->machine->properties["Model"]+" "+cpap->machine->properties["ModelNumber"]+submodel+"</td></tr>\n";
if (pref.Exists("ShowSerialNumbers") && pref["ShowSerialNumbers"].toBool()) {
html+="<tr><td colspan=4 align=center>"+cpap->machine->properties["Serial"]+"</td></tr>\n";
}
html+="<tr><td align='center'><b>Date</b></td><td align='center'><b>"+tr("Sleep")+"</b></td><td align='center'><b>"+tr("Wake")+"</b></td><td align='center'><b>"+tr("Hours")+"</b></td></tr>";
int tt=cpap->total_time()/1000.0;
QDateTime date=QDateTime::fromMSecsSinceEpoch(cpap->first());
QDateTime date2=QDateTime::fromMSecsSinceEpoch(cpap->last());
int h=tt/3600.0;
int m=(tt/60)%60;
int s=tt % 60;
html+=QString("<tr><td align='center'>%1</td><td align='center'>%2</td><td align='center'>%3</td><td align='center'>%4</td></tr>\n"
"<tr><td colspan=4 align=center><hr></td></tr>\n")
.arg(date.date().toString(Qt::SystemLocaleShortDate))
.arg(date.toString("HH:mm"))
.arg(date2.toString("HH:mm"))
.arg(QString().sprintf("%02i:%02i:%02i",h,m,s));
QString cs;
if (cpap->machine->GetClass()!="PRS1") {
cs="4 align=center>";
} else cs="2>";
html+="<tr><td colspan="+cs+"<table cellspacing=0 cellpadding=2 border=0 width='100%'>"
"<tr><td align='right' bgcolor='#F88017'><b><font color='black'>"+tr("AHI")+"</font></b></td><td bgcolor='#F88017'><b><font color='black'>"+QString().sprintf("%.2f",ahi)+"</font></b></td></tr>\n"
"<tr><td align='right' bgcolor='#4040ff'><b><font color='white'>"+tr("Hypopnea")+"</font></b></td><td bgcolor='#4040ff'><font color='white'>"+QString().sprintf("%.2f",hi)+"</font></td></tr>\n";
if (cpap->machine->GetClass()=="ResMed") {
html+="<tr><td align='right' bgcolor='#208020'><b>"+tr("Apnea")+"</b></td><td bgcolor='#208020'>"+QString().sprintf("%.2f",uai)+"</td></tr>\n";
}
html+="<tr><td align='right' bgcolor='#40afbf'><b>"+tr("Obstructive")+"</b></td><td bgcolor='#40afbf'>"+QString().sprintf("%.2f",oai)+"</td></tr>\n"
"<tr><td align='right' bgcolor='#b254cd'><b>"+tr("Clear Airway")+"</b></td><td bgcolor='#b254cd'>"+QString().sprintf("%.2f",cai)+"</td></tr>\n"
"</table></td>";
if (cpap->machine->GetClass()=="PRS1") {
html+="<td colspan=2><table cellspacing=0 cellpadding=2 border=0 width='100%'>"
"<tr><td align='right' bgcolor='#ffff80'><b>"+tr("RERA")+"</b></td><td bgcolor='#ffff80'>"+QString().sprintf("%.2f",rei)+"</td></tr>\n"
"<tr><td align='right' bgcolor='#404040'><b><font color='white'>"+tr("FlowLimit")+"</font></b></td><td bgcolor='#404040'><font color='white'>"+a.sprintf("%.2f",fli)+"</font></td></tr>\n"
"<tr><td align='right' bgcolor='#ff4040'><b>"+tr("Vsnore")+"</b></td><td bgcolor='#ff4040'>"+QString().sprintf("%.2f",vsi)+"</td></tr>\n"
"<tr><td align='right' bgcolor='#80ff80'><b>"+tr("PB/CSR")+"</b></td><td bgcolor='#80ff80'>"+QString().sprintf("%.2f",csr)+"%</td></tr>\n"
"</table></td>";
}
// Note, this may not be a problem since Qt bug 13622 was discovered
// as it only relates to text drawing, which the Pie chart does not do
// ^^ Scratch that.. pie now includes text..
if (pref["EnableGraphSnapshots"].toBool()) { // AHI Pie Chart
html+="</tr>\n<tr><td colspan=4 align=center><i>"+tr("Event Breakdown")+"</i></td></tr>\n";
G_AHI->setFixedSize(gwwidth,120);
QPixmap pixmap=G_AHI->renderPixmap(gwwidth,120,false); //gwwidth,gwheight,false);
QByteArray byteArray;
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG");
html += "<tr><td colspan=4 align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\"></td></tr>\n";
}
}
html+="</table>"
"<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
if (cpap || oxi) {
html+="<tr height='2'><td colspan=5 height='2'><hr></td></tr>\n";
/* if (mode==MODE_BIPAP) {
html+="<tr><td colspan=4 align='center'><i>"+tr("90%&nbsp; EPAP ")+QString().sprintf("%.2f",eap90)+tr("cmH2O")+"</td></tr>\n"
"<tr><td colspan=4 align='center'><i>"+tr("90%&nbsp; IPAP ")+QString().sprintf("%.2f",iap90)+tr("cmH2O")+"</td></tr>\n";
} else if (mode==MODE_APAP) {
html+=("<tr><td colspan=4 align='center'><i>")+tr("90%&nbsp; Auto Pressure ")+QString().sprintf("%.2f",p90)+("</i></td></tr>\n"); //cpap->summary_weighted_avg(CPAP_PressurePercentValue)
} else if (mode==MODE_CPAP) {
html+=("<tr><td colspan=4 align='center'><i>")+tr("CPAP Pressure ")+QString().sprintf("%.2f",cpap->max(CPAP_Pressure))+("</i></td></tr>\n");
}*/
//html+=("<tr><td colspan=4 align=center>&nbsp;</td></tr>\n");
html+=("<tr><td> </td><td><b>Min</b></td><td><b>Avg</b></td><td><b>90%</b></td><td><b>Max</b></td></tr>");
ChannelID chans[]={CPAP_Pressure,CPAP_EPAP,CPAP_IPAP,CPAP_PressureSupport,CPAP_PatientTriggeredBreaths, CPAP_MinuteVentilation,CPAP_RespiratoryRate,CPAP_FlowLimitGraph,CPAP_Leak,CPAP_Snore,CPAP_TidalVolume,CPAP_Pulse,CPAP_SPO2,OXI_Pulse,OXI_SPO2};
int numchans=sizeof(chans)/sizeof(ChannelID);
for (int i=0;i<numchans;i++) {
ChannelID code=chans[i];
if (cpap && cpap->channelExists(code)) {
html+="<tr><td align=left>"+channel[code].label();
html+="</td><td>"+a.sprintf("%.2f",cpap->min(code));
html+="</td><td>"+a.sprintf("%.2f",cpap->wavg(code));
html+="</td><td>"+a.sprintf("%.2f",cpap->p90(code));
html+="</td><td>"+a.sprintf("%.2f",cpap->max(code));
html+="</td><tr>";
}
if (oxi && oxi->channelExists(code)) {
html+="<tr><td align=left>"+channel[code].label();
html+="</td><td>"+a.sprintf("%.2f",oxi->min(code));
html+="</td><td>"+a.sprintf("%.2f",oxi->wavg(code));
html+="</td><td>"+a.sprintf("%.2f",oxi->p90(code));
html+="</td><td>"+a.sprintf("%.2f",oxi->max(code));
html+="</td><tr>";
}
}
} else {
html+="<tr><td colspan=5 align=center><i>"+tr("No data available")+"</i></td></tr>";
html+="<tr><td colspan=5>&nbsp;</td></tr>\n";
}
html+="</table>";
html+="<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
if (cpap) {
if (pref["EnableGraphSnapshots"].toBool()) {
if (cpap->channelExists(CPAP_Pressure)) {
html+=("<tr><td colspan=4 align=center><i>")+tr("Time@Pressure")+("</i></td></tr>\n");
TAP->setFixedSize(gwwidth,30);
QPixmap pixmap=TAP->renderPixmap(gwwidth,30,false);
QByteArray byteArray;
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG");
html+="<tr><td colspan=4 align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\"></td></tr>\n";
}
if (cpap->channelExists(CPAP_EPAP)) {
//html+="<tr height='2'><td colspan=4 height='2'><hr></td></tr>\n";
html+=("<tr><td colspan=4 align=center><i>")+tr("Time@EPAP")+("</i></td></tr>\n");
TAP_EAP->setFixedSize(gwwidth,30);
QPixmap pixmap=TAP_EAP->renderPixmap(gwwidth,30,false);
QByteArray byteArray;
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG");
html+="<tr><td colspan=4 align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\"></td></tr>\n";
}
if (cpap->channelExists(CPAP_IPAP)) {
html+=("<tr><td colspan=4 align=center><i>")+tr("Time@IPAP")+("</i></td></tr>\n");
TAP_IAP->setFixedSize(gwwidth,30);
QPixmap pixmap=TAP_IAP->renderPixmap(gwwidth,30,false);
QByteArray byteArray;
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG");
html+="<tr><td colspan=4 align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\"></td></tr>\n";
}
}
html+="</table><hr height=2><table cellpadding=0 cellspacing=0 border=0 width=100%>";
html+="<tr><td align=center>SessionID</td><td align=center>Date</td><td align=center>Start</td><td align=center>End</td></tr>";
QDateTime fd,ld;
bool corrupted_waveform=false;
for (QVector<Session *>::iterator s=cpap->begin();s!=cpap->end();s++) {
fd=QDateTime::fromMSecsSinceEpoch((*s)->first());
ld=QDateTime::fromMSecsSinceEpoch((*s)->last());
QHash<ChannelID,QVariant>::iterator i=(*s)->settings.find(CPAP_BrokenWaveform);
if ((i!=(*s)->settings.end()) && i.value().toBool()) corrupted_waveform=true;
tmp.sprintf(("<tr><td align=center>%08i</td><td align=center>"+fd.date().toString(Qt::SystemLocaleShortDate)+"</td><td align=center>"+fd.toString("HH:mm ")+"</td><td align=center>"+ld.toString("HH:mm")+"</td></tr>").toLatin1(),(*s)->session());
html+=tmp;
}
html+="</table>";
if (corrupted_waveform) {
html+="<hr><div align=center><i>One or more waveform record for this session had faulty source data. Some waveform overlay points may not match up correctly.</i></div>";
}
}
html+="</html>";
ui->webView->setHtml(html);
ui->JournalNotes->clear();
Session *journal=GetJournalSession(date);
if (journal) {
ui->JournalNotes->setHtml(journal->settings[JOURNAL_Notes].toString());
}
}
void Daily::Unload(QDate date)
{
Session *journal=GetJournalSession(date);
if (!ui->JournalNotes->toPlainText().isEmpty()) {
QString jhtml=ui->JournalNotes->toHtml();
if (journal) {
if (journal->settings[JOURNAL_Notes]!=jhtml) {
journal->settings[JOURNAL_Notes]=jhtml;
journal->SetChanged(true);
}
} else {
journal=CreateJournalSession(date);
journal->settings[JOURNAL_Notes]=jhtml;
journal->SetChanged(true);
}
}
if (journal) {
Machine *jm=profile->GetMachine(MT_JOURNAL);
if (jm) jm->SaveSession(journal);
}
UpdateCalendarDay(date);
}
void Daily::on_JournalNotesItalic_clicked()
{
QTextCursor cursor = ui->JournalNotes->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
QTextCharFormat format=cursor.charFormat();
format.setFontItalic(!format.fontItalic());
cursor.mergeCharFormat(format);
//ui->JournalNotes->mergeCurrentCharFormat(format);
}
void Daily::on_JournalNotesBold_clicked()
{
QTextCursor cursor = ui->JournalNotes->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
QTextCharFormat format=cursor.charFormat();
int fw=format.fontWeight();
if (fw!=QFont::Bold)
format.setFontWeight(QFont::Bold);
else
format.setFontWeight(QFont::Normal);
cursor.mergeCharFormat(format);
//ui->JournalNotes->mergeCurrentCharFormat(format);
}
void Daily::on_JournalNotesFontsize_activated(int index)
{
QTextCursor cursor = ui->JournalNotes->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
QTextCharFormat format=cursor.charFormat();
QFont font=format.font();
int fontsize=10;
if (index==1) fontsize=15;
else if (index==2) fontsize=25;
font.setPointSize(fontsize);
format.setFont(font);
cursor.mergeCharFormat(format);
}
void Daily::on_JournalNotesColour_clicked()
{
QColor col=QColorDialog::getColor(Qt::black,this,tr("Pick a Colour")); //,QColorDialog::NoButtons);
if (!col.isValid()) return;
QTextCursor cursor = ui->JournalNotes->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
QBrush b(col);
QPalette newPalette = palette();
newPalette.setColor(QPalette::ButtonText, col);
ui->JournalNotesColour->setPalette(newPalette);
QTextCharFormat format=cursor.charFormat();
format.setForeground(b);
cursor.setCharFormat(format);
}
Session * Daily::CreateJournalSession(QDate date)
{
Machine *m=profile->GetMachine(MT_JOURNAL);
if (!m) {
m=new Machine(profile,0);
m->SetClass("Journal");
m->properties["Brand"]="Virtual";
m->SetType(MT_JOURNAL);
profile->AddMachine(m);
}
Session *sess=new Session(m,0);
QDateTime dt;
dt.setDate(date);
dt.setTime(QTime(17,0)); //5pm to make sure it goes in the right day
sess->set_first(dt.toMSecsSinceEpoch());
dt=dt.addSecs(3600);
sess->set_last(dt.toMSecsSinceEpoch());
sess->SetChanged(true);
m->AddSession(sess,profile);
return sess;
}
Session * Daily::GetJournalSession(QDate date) // Get the first journal session
{
Day *journal=profile->GetDay(date,MT_JOURNAL);
if (!journal)
return NULL; //CreateJournalSession(date);
QVector<Session *>::iterator s;
s=journal->begin();
if (s!=journal->end())
return *s;
return NULL;
}
void Daily::on_EnergySlider_sliderMoved(int position)
{
//Session *s=GetJournalSession(previous_date);
//if (!s)
// s=CreateJournalSession(previous_date);
//s->summary[JOURNAL_Energy]=position;
//s->SetChanged(true);
}
void Daily::UpdateCPAPGraphs(Day *day)
{
//if (!day) return;
if (day) {
day->OpenEvents();
}
for (QList<gLayer *>::iterator g=CPAPData.begin();g!=CPAPData.end();g++) {
(*g)->SetDay(day);
}
};
void Daily::UpdateOXIGraphs(Day *day)
{
//if (!day) return;
if (day) {
day->OpenEvents();
}
for (QList<gLayer *>::iterator g=OXIData.begin();g!=OXIData.end();g++) {
(*g)->SetDay(day);
}
}
void Daily::RedrawGraphs()
{
for (int i=0;i<Graphs.size();i++) {
Graphs[i]->updateGL();
}
}
void Daily::on_treeWidget_itemSelectionChanged()
{
if (ui->treeWidget->selectedItems().size()==0) return;
QTreeWidgetItem *item=ui->treeWidget->selectedItems().at(0);
if (!item) return;
QDateTime d;
if (!item->text(1).isEmpty()) {
d=d.fromString(item->text(1),"yyyy-MM-dd HH:mm:ss");
double st=(d.addSecs(-120)).toMSecsSinceEpoch();
double et=(d.addSecs(120)).toMSecsSinceEpoch();
FRW->SetXBounds(st,et);
MP->SetXBounds(st,et);
SF->SetXBounds(st,et);
PRD->SetXBounds(st,et);
LEAK->SetXBounds(st,et);
SNORE->SetXBounds(st,et);
MV->SetXBounds(st,et);
TV->SetXBounds(st,et);
RR->SetXBounds(st,et);
FLG->SetXBounds(st,et);
}
}
void Daily::on_JournalNotesUnderline_clicked()
{
QTextCursor cursor = ui->JournalNotes->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
QTextCharFormat format=cursor.charFormat();
format.setFontUnderline(!format.fontUnderline());
cursor.mergeCharFormat(format);
//ui->JournalNotes->mergeCurrentCharFormat(format);
}