From 0bf8d8833a5fbb0f4f01659669f93be18e6a39e8 Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Sun, 20 Jul 2014 19:22:31 +1000 Subject: [PATCH] Add time display and verticle line in alt-select mode, plus userflags in piechart option --- sleepyhead/Graphs/gGraph.cpp | 4 +- sleepyhead/Graphs/gGraphView.cpp | 17 +++++--- sleepyhead/Graphs/gLineChart.cpp | 29 +++++++++++++ sleepyhead/Graphs/gLineChart.h | 3 ++ sleepyhead/Graphs/gLineOverlay.cpp | 5 ++- sleepyhead/SleepLib/calcs.cpp | 65 +++++++++++++++++++++++++++--- sleepyhead/SleepLib/calcs.h | 2 +- sleepyhead/SleepLib/profiles.h | 5 +++ sleepyhead/SleepLib/schema.cpp | 4 +- sleepyhead/daily.cpp | 12 ++++-- sleepyhead/preferencesdialog.cpp | 11 ++++- sleepyhead/preferencesdialog.ui | 11 ++++- 12 files changed, 142 insertions(+), 26 deletions(-) diff --git a/sleepyhead/Graphs/gGraph.cpp b/sleepyhead/Graphs/gGraph.cpp index 83439b38..6c8414f2 100644 --- a/sleepyhead/Graphs/gGraph.cpp +++ b/sleepyhead/Graphs/gGraph.cpp @@ -792,7 +792,7 @@ void gGraph::mouseMoveEvent(QMouseEvent *event) //if (!nolayer) { // no mouse button if (doredraw) { - m_graphview->redraw(); + m_graphview->timedRedraw(30); } //} @@ -1060,7 +1060,7 @@ void gGraph::keyReleaseEvent(QKeyEvent *event) if (!m_graphview) return; if (m_graphview->selectionInProgress() && m_graphview->metaSelect()) { - if (!(event->modifiers() & Qt::ShiftModifier)) { + if (!(event->modifiers() & Qt::AltModifier)) { } } diff --git a/sleepyhead/Graphs/gGraphView.cpp b/sleepyhead/Graphs/gGraphView.cpp index e4f25fc6..0549ec4f 100644 --- a/sleepyhead/Graphs/gGraphView.cpp +++ b/sleepyhead/Graphs/gGraphView.cpp @@ -1094,6 +1094,8 @@ void gGraphView::setOffsetX(int offsetX) void gGraphView::mouseMoveEvent(QMouseEvent *event) { + this->setFocus(); + int x = event->x(); int y = event->y(); @@ -1397,7 +1399,7 @@ void gGraphView::mousePressEvent(QMouseEvent *event) m_button_down = true; - m_metaselect = event->modifiers() && Qt::ShiftModifier; + m_metaselect = event->modifiers() && Qt::AltModifier; m_horiz_travel = 0; m_graph_index = i; m_selected_graph = m_graphs[i]; @@ -1459,7 +1461,7 @@ void gGraphView::mousePressEvent(QMouseEvent *event) m_point_clicked = QPoint(event->x(), event->y()); //QMouseEvent e(event->type(),m_point_clicked,event->button(),event->buttons(),event->modifiers()); m_button_down = true; - m_metaselect = event->modifiers() && Qt::ShiftModifier; + m_metaselect = event->modifiers() && Qt::AltModifier; m_horiz_travel = 0; m_graph_index = i; @@ -1573,7 +1575,7 @@ void gGraphView::mouseReleaseEvent(QMouseEvent *event) // The graph that got the button press gets the release event if (m_button_down) { m_button_down = false; - m_metaselect = event->modifiers() & Qt::ShiftModifier; + m_metaselect = event->modifiers() & Qt::AltModifier; saveHistory(); if (m_metaselect) { @@ -1586,7 +1588,7 @@ void gGraphView::mouseReleaseEvent(QMouseEvent *event) void gGraphView::keyReleaseEvent(QKeyEvent *event) { - if (m_metaselect && !(event->modifiers() & Qt::ShiftModifier)) { + if (m_metaselect && !(event->modifiers() & Qt::AltModifier)) { QMouseEvent mevent(QEvent::MouseButtonRelease, m_point_released, Qt::LeftButton, Qt::LeftButton, event->modifiers()); if (m_graph_index>=0) m_graphs[m_graph_index]->mouseReleaseEvent(&mevent); @@ -1819,8 +1821,11 @@ void gGraphView::wheelEvent(QWheelEvent *event) void gGraphView::keyPressEvent(QKeyEvent *event) { - if (m_button_down) { - m_metaselect = event->modifiers() & Qt::ShiftModifier; + bool meta = m_metaselect; + m_metaselect = event->modifiers() & Qt::AltModifier; + + if (meta != m_metaselect) { + timedRedraw(30); } if (event->key() == Qt::Key_Tab) { diff --git a/sleepyhead/Graphs/gLineChart.cpp b/sleepyhead/Graphs/gLineChart.cpp index 11be4c11..14b54c14 100644 --- a/sleepyhead/Graphs/gLineChart.cpp +++ b/sleepyhead/Graphs/gLineChart.cpp @@ -157,6 +157,14 @@ EventDataType gLineChart::Maxy() return Layer::Maxy() - subtract_offset; } +bool gLineChart::mouseMoveEvent(QMouseEvent *event, gGraph *graph) +{ + Q_UNUSED(event) + if (graph->graphView()->metaSelect()) + graph->timedRedraw(30); + return true; +} + // Time Domain Line Chart void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) { @@ -226,6 +234,27 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) } } + if (w.graphView()->metaSelect()) { + QPoint mouse = w.graphView()->currentMousePos(); + double pos = mouse.x() - left; + if (pos > 0) { + double xval = minx + (pos * (xx / double(width))); + + + painter.setPen(QPen(QBrush(QColor(Qt::gray)),1)); + painter.drawLine(mouse.x(), top-w.marginTop()-3, mouse.x(), top+height+w.bottom-1); + + QDateTime dt=QDateTime::fromMSecsSinceEpoch(xval); + + QString text = dt.toString("MMM dd - HH:mm:ss:zzz"); + + int wid, h; + GetTextExtent(text, wid, h); + w.renderText(text, left + width/2 - wid/2, top -h); + } + + } + EventDataType lastpx, lastpy; EventDataType px, py; int idx; diff --git a/sleepyhead/Graphs/gLineChart.h b/sleepyhead/Graphs/gLineChart.h index 96d1d39d..329d3b49 100644 --- a/sleepyhead/Graphs/gLineChart.h +++ b/sleepyhead/Graphs/gLineChart.h @@ -79,6 +79,9 @@ class gLineChart: public Layer void setPlotEnabled(ChannelID code, bool b) { m_enabled[code] = b; } protected: + //! \brief Mouse moved over this layers area (shows the hover-over tooltips here) + virtual bool mouseMoveEvent(QMouseEvent *event, gGraph *graph); + bool m_report_empty; bool m_square_plot; bool m_disable_accel; diff --git a/sleepyhead/Graphs/gLineOverlay.cpp b/sleepyhead/Graphs/gLineOverlay.cpp index 90004ef6..141099d2 100644 --- a/sleepyhead/Graphs/gLineOverlay.cpp +++ b/sleepyhead/Graphs/gLineOverlay.cpp @@ -21,6 +21,8 @@ gLineOverlayBar::~gLineOverlayBar() { } +QColor brighten(QColor color); + void gLineOverlayBar::paint(QPainter &painter, gGraph &w, const QRegion ®ion) { int left = region.boundingRect().left(); @@ -145,10 +147,10 @@ void gLineOverlayBar::paint(QPainter &painter, gGraph &w, const QRegion ®ion) if (x1 > width + left) { x1 = width + left; } + QRect rect(x2, start_py, x1-x2, height); QColor col = m_flag_color; if (rect.contains(mouse)) { - col = QColor("gold"); hover = true; } @@ -263,7 +265,6 @@ void gLineOverlayBar::paint(QPainter &painter, gGraph &w, const QRegion ®ion) bool gLineOverlayBar::mouseMoveEvent(QMouseEvent *event, gGraph *graph) { Q_UNUSED(event) - graph->timedRedraw(0); return true; } diff --git a/sleepyhead/SleepLib/calcs.cpp b/sleepyhead/SleepLib/calcs.cpp index 327459b8..4829047d 100644 --- a/sleepyhead/SleepLib/calcs.cpp +++ b/sleepyhead/SleepLib/calcs.cpp @@ -21,18 +21,71 @@ extern double round(double number); -bool SearchApnea(Session *session, qint64 time, qint64 dist) + +bool SearchEvent(Session * session, ChannelID code, qint64 time, EventStoreType dur, qint64 dist) { - if (session->SearchEvent(CPAP_Obstructive, time, dist)) + qint64 t, start; + QHash >::iterator it; + it = session->eventlist.find(code); + quint32 *tptr; + + EventStoreType *dptr; + + int cnt; + + //qint64 rate; + + QHash >::iterator evend = session->eventlist.end(); + if (it != evend) { + int el_size=it.value().size(); + for (int i = 0; i < el_size; i++) { + EventList *el = it.value()[i]; + // rate=el->rate(); + cnt = el->count(); + + // why would this be necessary??? + if (el->type() == EVL_Waveform) { + qDebug() << "Called SearchEvent on a waveform object!"; + return false; + } else { + start = el->first(); + tptr = el->rawTime(); + dptr = el->rawData(); + + for (int j = 0; j < cnt; j++) { + t = start + *tptr; + + if (qAbs(time - t) < dist) { + + // Move the position and set the duration + if (dur>0) { + *tptr = time - start; + *dptr = (EventStoreType)dur; + } + return true; + } + tptr++; + dptr++; + } + } + } + } + + return false; +} + +bool SearchApnea(Session *session, qint64 time, double dur, qint64 dist) +{ + if (SearchEvent(session, CPAP_Obstructive, time, dur, dist)) return true; - if (session->SearchEvent(CPAP_Apnea, time, dist)) + if (SearchEvent(session, CPAP_Apnea, time, dur, dist)) return true; - if (session->SearchEvent(CPAP_ClearAirway, time, dist)) + if (SearchEvent(session, CPAP_ClearAirway, time, dur, dist)) return true; - if (session->SearchEvent(CPAP_Hypopnea, time, dist)) + if (SearchEvent(session, CPAP_Hypopnea, time, 0, dist)) return true; if (session->SearchEvent(CPAP_UserFlag1, time, dist)) @@ -724,7 +777,7 @@ void FlowParser::flagUserEvents(ChannelID code, EventDataType restriction, Event dur = len / 1000.0; if (dur >= duration) { - if (allowDuplicates || !SearchApnea(m_session, et - len / 2, 15000)) { + if (allowDuplicates || !SearchApnea(m_session, et, dur, 15000)) { if (!uf) { uf = m_session->AddEventList(code, EVL_Event); } diff --git a/sleepyhead/SleepLib/calcs.h b/sleepyhead/SleepLib/calcs.h index 066eea3d..a7c7505c 100644 --- a/sleepyhead/SleepLib/calcs.h +++ b/sleepyhead/SleepLib/calcs.h @@ -140,7 +140,7 @@ class FlowParser EventDataType *m_buffers[num_filter_buffers]; }; -bool SearchApnea(Session *session, qint64 time, qint64 dist = 15000); +bool SearchApnea(Session *session, qint64 time, double dur, qint64 dist = 15000); //! \brief Calculate Respiratory Rate, Tidal Volume & Minute Ventilation for PRS1 data void calcRespRate(Session *session, FlowParser *flowparser = nullptr); diff --git a/sleepyhead/SleepLib/profiles.h b/sleepyhead/SleepLib/profiles.h index 436de67d..714d5a4f 100644 --- a/sleepyhead/SleepLib/profiles.h +++ b/sleepyhead/SleepLib/profiles.h @@ -271,6 +271,8 @@ const QString STR_CS_UntreatedAHI = "UntreatedAHI"; const QString STR_CS_Notes = "CPAPNotes"; const QString STR_CS_DateDiagnosed = "DateDiagnosed"; const QString STR_CS_UserEventFlagging = "UserEventFlagging"; +const QString STR_CS_UserEventPieChart = "UserEventPieChart"; + const QString STR_CS_UserFlowRestriction = "UserFlowRestriction"; const QString STR_CS_UserEventDuration = "UserEventDuration"; const QString STR_CS_UserFlowRestriction2 = "UserFlowRestriction2"; @@ -525,6 +527,7 @@ class CPAPSettings : public ProfileSettings initPref(STR_CS_AHIReset, false); initPref(STR_CS_LeakRedline, 24.0); initPref(STR_CS_ShowLeakRedline, true); + initPref(STR_CS_UserEventPieChart, false); initPref(STR_CS_ClockDrift, (int)0); m_clock_drift = getPref(STR_CS_ClockDrift).toInt(); @@ -554,6 +557,7 @@ class CPAPSettings : public ProfileSettings int clockDrift() const { return m_clock_drift; } EventDataType leakRedline() const { return getPref(STR_CS_LeakRedline).toFloat(); } bool showLeakRedline() const { return getPref(STR_CS_ShowLeakRedline).toBool(); } + bool userEventPieChart() const { return getPref(STR_CS_UserEventPieChart).toBool(); } //Setters void setMode(CPAPMode mode) { setPref(STR_CS_PrescribedMode, (int)mode); } @@ -583,6 +587,7 @@ class CPAPSettings : public ProfileSettings } void setLeakRedline(EventDataType value) { setPref(STR_CS_LeakRedline, value); } void setShowLeakRedline(bool reset) { setPref(STR_CS_ShowLeakRedline, reset); } + void setUserEventPieChart(bool b) { setPref(STR_CS_UserEventPieChart, b); } public: int m_clock_drift; diff --git a/sleepyhead/SleepLib/schema.cpp b/sleepyhead/SleepLib/schema.cpp index 2f4e6462..c0e4b045 100644 --- a/sleepyhead/SleepLib/schema.cpp +++ b/sleepyhead/SleepLib/schema.cpp @@ -211,12 +211,12 @@ void init() schema::channel.add(GRP_CPAP, new Channel(CPAP_UserFlag1 = 0x101e, FLAG, SESSION, "UserFlag1", QObject::tr("User Flag #1"), QObject::tr("A user definable event detected by SleepyHead's flow waveform processor."), - QObject::tr("UF1"), STR_UNIT_EventsPerHour, DEFAULT, QColor("dark cyan"))); + QObject::tr("UF1"), STR_UNIT_EventsPerHour, DEFAULT, QColor(0xc0,0xc0,0xe0))); schema::channel.add(GRP_CPAP, new Channel(CPAP_UserFlag2 = 0x101f, FLAG, SESSION, "UserFlag2", QObject::tr("User Flag #2"), QObject::tr("A user definable event detected by SleepyHead's flow waveform processor."), - QObject::tr("UF2"), STR_UNIT_EventsPerHour, DEFAULT, QColor("dark blue"))); + QObject::tr("UF2"), STR_UNIT_EventsPerHour, DEFAULT, QColor(0xa0,0xa0,0xc0))); schema::channel.add(GRP_CPAP, new Channel(CPAP_UserFlag3 = 0x1024, FLAG, SESSION, "UserFlag3", QObject::tr("User Flag #3"), diff --git a/sleepyhead/daily.cpp b/sleepyhead/daily.cpp index 9a02366a..5c057f41 100644 --- a/sleepyhead/daily.cpp +++ b/sleepyhead/daily.cpp @@ -192,7 +192,10 @@ Daily::Daily(QWidget *parent,gGraphView * shared) evseg->AddSlice(CPAP_NRI,QColor(0x00,0x80,0x40,0xff),STR_TR_NR); evseg->AddSlice(CPAP_FlowLimit,QColor(0x40,0x40,0x40,0xff),STR_TR_FL); evseg->AddSlice(CPAP_SensAwake,QColor(0x40,0xC0,0x40,0xff),STR_TR_SA); - //evseg->AddSlice(CPAP_UserFlag1,QColor(0x40,0x40,0x40,0xff),tr("UF")); + if (p_profile->cpap->userEventPieChart()) { + evseg->AddSlice(CPAP_UserFlag1,QColor(0xe0,0xe0,0xe0,0xff),tr("UF1")); + evseg->AddSlice(CPAP_UserFlag2,QColor(0xc0,0xc0,0xe0,0xff),tr("UF2")); + } GAHI->AddLayer(AddCPAP(evseg)); GAHI->setMargins(0,0,0,0); @@ -1393,8 +1396,6 @@ void Daily::Load(QDate date) { CPAP_Apnea, COLOR_Apnea, Qt::black, uai=cpap->count(CPAP_Apnea)/hours }, { CPAP_ClearAirway, COLOR_ClearAirway, Qt::black, cai=cpap->count(CPAP_ClearAirway)/hours }, { CPAP_NRI, COLOR_NRI, Qt::black, nri=cpap->count(CPAP_NRI)/hours }, - { CPAP_UserFlag1, COLOR_UserFlag1, Qt::black, uf1=cpap->count(CPAP_UserFlag1)/hours }, - { CPAP_UserFlag2, COLOR_UserFlag2, Qt::black, uf2=cpap->count(CPAP_UserFlag2)/hours }, { CPAP_FlowLimit, COLOR_FlowLimit, Qt::white, fli=cpap->count(CPAP_FlowLimit)/hours }, { CPAP_SensAwake, COLOR_SensAwake, Qt::black, sai=cpap->count(CPAP_SensAwake)/hours }, { CPAP_ExP, COLOR_ExP, Qt::black, exp=cpap->count(CPAP_ExP)/hours }, @@ -1403,7 +1404,10 @@ void Daily::Load(QDate date) { CPAP_VSnore2, COLOR_VibratorySnore, Qt::black, vs2=cpap->count(CPAP_VSnore2)/cpap->hours() }, { CPAP_LeakFlag, COLOR_LeakFlag, Qt::black, lki=cpap->count(CPAP_LeakFlag)/hours }, { CPAP_LargeLeak, COLOR_LargeLeak, Qt::black, lk2=(100.0/cpap->hours())*(cpap->sum(CPAP_LargeLeak)/3600.0) }, - { CPAP_CSR, COLOR_CSR, Qt::black, csr=(100.0/cpap->hours())*(cpap->sum(CPAP_CSR)/3600.0) } + { CPAP_CSR, COLOR_CSR, Qt::black, csr=(100.0/cpap->hours())*(cpap->sum(CPAP_CSR)/3600.0) }, + { CPAP_UserFlag1, COLOR_UserFlag1, Qt::black, uf1=cpap->count(CPAP_UserFlag1)/hours }, + { CPAP_UserFlag2, COLOR_UserFlag2, Qt::black, uf2=cpap->count(CPAP_UserFlag2)/hours }, + }; int numchans=sizeof(chans)/sizeof(ChannelInfo); diff --git a/sleepyhead/preferencesdialog.cpp b/sleepyhead/preferencesdialog.cpp index 74a865d6..b92dc74b 100644 --- a/sleepyhead/preferencesdialog.cpp +++ b/sleepyhead/preferencesdialog.cpp @@ -247,6 +247,8 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : ui->userEventDuplicates->setChecked(profile->cpap->userEventDuplicates()); ui->userEventDuplicates->setVisible(false); + ui->showUserFlagsInPie->setChecked(profile->cpap->userEventPieChart()); + ui->eventTable->setColumnWidth(0, 40); ui->eventTable->setColumnWidth(1, 55); ui->eventTable->setColumnHidden(3, true); @@ -344,6 +346,11 @@ bool PreferencesDialog::Save() needs_restart = true; } + if (profile->cpap->userEventPieChart() != ui->showUserFlagsInPie->isChecked()) { + // lazy.. fix me + needs_restart = true; + } + if (profile->general->calculateRDI() != ui->AddRERAtoAHI->isChecked()) { //recalc_events=true; needs_restart = true; @@ -393,6 +400,8 @@ bool PreferencesDialog::Save() } } + profile->cpap->setUserEventPieChart(ui->showUserFlagsInPie->isChecked()); + profile->appearance->setAllowYAxisScaling(ui->allowYAxisScaling->isChecked()); profile->appearance->setGraphTooltips(ui->graphTooltips->isChecked()); @@ -551,11 +560,11 @@ bool PreferencesDialog::Save() PREF.Save(); p_profile->Save(); - if (recalc_events) { // send a signal instead? mainwin->reprocessEvents(needs_restart); } else if (needs_restart) { + p_profile->removeLock(); mainwin->RestartApplication(); } else { mainwin->getDaily()->LoadDate(mainwin->getDaily()->getDate()); diff --git a/sleepyhead/preferencesdialog.ui b/sleepyhead/preferencesdialog.ui index c772f102..8caa8395 100644 --- a/sleepyhead/preferencesdialog.ui +++ b/sleepyhead/preferencesdialog.ui @@ -992,6 +992,13 @@ p, li { white-space: pre-wrap; } + + + + Allow duplicates near machine events. + + + @@ -1010,9 +1017,9 @@ p, li { white-space: pre-wrap; } - + - Allow duplicates near machine events. + Show in Event Breakdown Piechart