/* MinutesAtPressure Graph Implementation * * Copyright (c) 2011-2018 Mark Watkins * * 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. */ #include #include #include #include #include "MinutesAtPressure.h" #include "Graphs/gGraph.h" #include "Graphs/gGraphView.h" #include "SleepLib/profiles.h" #include "Graphs/gXAxis.h" #include "Graphs/gYAxis.h" MinutesAtPressure::MinutesAtPressure() :Layer(NoChannel) { m_remap = nullptr; m_minpressure = 3; m_maxpressure = 30; m_minimum_height = 0; } MinutesAtPressure::~MinutesAtPressure() { while (recalculating()) {}; } RecalcMAP::~RecalcMAP() { } void RecalcMAP::quit() { map->mutex.lock(); m_quit = true; map->mutex.unlock(); } void MinutesAtPressure::SetDay(Day *day) { Layer::SetDay(day); // look at session summaryValues. Machine * cpap = nullptr; if (day) cpap = day->machine(MT_CPAP); if (cpap) { EventDataType minpressure = 20; EventDataType maxpressure = 0; // look at overall pressure ranges and find the max for (const auto d : cpap->day) { for (const auto sess : d->sessions) { if (sess->channelExists(CPAP_Pressure)) { minpressure = qMin(sess->Min(CPAP_Pressure), minpressure); maxpressure = qMax(sess->Max(CPAP_Pressure), maxpressure); } if (sess->channelExists(CPAP_EPAP)) { minpressure = qMin(sess->Min(CPAP_EPAP), minpressure); maxpressure = qMax(sess->Max(CPAP_EPAP), maxpressure); } if (sess->channelExists(CPAP_IPAP)) { minpressure = qMin(sess->Min(CPAP_IPAP), minpressure); maxpressure = qMax(sess->Max(CPAP_IPAP), maxpressure); } } } m_minpressure = floor(minpressure)-1; m_maxpressure = ceil(maxpressure); /* const int minimum_cells = 12; int c = m_maxpressure - m_minpressure; if (c < minimum_cells) { int v = minimum_cells - c; m_minpressure -= v/2.0; m_minpressure = qMax((EventStoreType)4, m_minpressure); m_maxpressure = m_minpressure + minimum_cells; } */ // QFontMetrics FM(*defaultfont); // quint32 chantype = schema::SPAN | schema::FLAG | schema::MINOR_FLAG; // QList chans = day->getSortedMachineChannels(chantype); // m_minimum_height = (chans.size()+3) * FM.height() - 5; } m_empty = false; m_recalculating = false; m_lastminx = 0; m_lastmaxx = 0; m_empty = !m_day || !(m_day->channelExists(CPAP_Pressure) || m_day->channelExists(CPAP_EPAP)); } int MinutesAtPressure::minimumHeight() { return m_minimum_height; } bool MinutesAtPressure::isEmpty() { return m_empty; } float pressureMult = 5; void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion ®ion) { QRectF rect = region.boundingRect(); rect.translate(0.0f, 0.001f); //int cells = m_maxpressure-m_minpressure+1; int top = rect.top()-10; float width = rect.width(); float height = rect.height(); float left = rect.left(); //float pix = width / float(cells); float bottom = rect.bottom(); //int numchans = chans.size(); //int cells_high = numchans + 2; //height += 10; //float hix = height / cells_high; m_minx = graph.min_x; m_maxx = graph.max_x; if (graph.printing() || ((m_lastminx != m_minx) || (m_lastmaxx != m_maxx))) { // note: this doesn't run on popout graphs that aren't linked with scrolling... // it's a pretty useless graph to popout, probably should just block it's popout instead. recalculate(&graph); } m_lastminx = m_minx; m_lastmaxx = m_maxx; if (graph.printing()) { // Could just lock the mutex QMutex instead mutex.lock(); // do nothing between, it should hang until complete. mutex.unlock(); //while (recalculating()) { QThread::yieldCurrentThread(); } // yield or whatever } if (!painter.isActive()) return; // Recalculating in the background... So we just draw an old copy until then the new data is ready // (it will refresh itself when complete) // The only time we can't draw when at the end of the recalc when the map variables are being updated // So use a mutex to lock QMutexLocker TimeLock(&timelock); painter.setFont(*defaultfont); painter.setPen(Qt::black); painter.drawRect(rect);//rect.left(),rect.top(), rect.width(), height+1); int minpressure = qMin((EventStoreType)4, m_minpressure); int maxpressure = qMax((EventStoreType)16, m_maxpressure); int min = minpressure * pressureMult; int max = maxpressure * pressureMult; int tot = max - min; double xstep = double(width) / double(tot); height -= 2; double peak = double(qMax(ipap.peaktime, epap.peaktime))/60.0; int w, h; GetTextExtent("9", w, h); double peakstep = 1.0; double peakmult = double(height+2) / (peak); if (peakmult < h+4) { peakstep = 5.0; peakmult = double(height+2) / (peak/peakstep); if (peakmult < h+4) { peakstep = 10.0; peakmult = double(height+2) / (peak/peakstep); if (peakmult < h+4) { peakstep = 20.0; peakmult = double(height+2) / (peak/peakstep); if (peakmult < h+4) { peakstep = 50.0; peakmult = double(height+2) / (peak/peakstep); if (peakmult < h+4) { peakstep = 100.0; peakmult = double(height+2) / (peak/peakstep); } } } } } // Now round peak up peak = ceil(peak/peakstep)*peakstep; // recalculate peakmult using rounded up figure peakmult = double(height+2)/ (peak / peakstep); m_miny = m_physminy = 0; m_maxy = m_physmaxy = peak; double ystep = double(height) / peak; EventDataType p0, p1, p2, p3; QString label; double s2; int widest_YAxis = 0; float lineThickness = AppSetting->lineThickness(); int mouseOverKey = 0; if (ipap.min_pressure > 0) { double xp,yp; //////////////////////////////////////////////////////////////////// // Draw X Axis labels //////////////////////////////////////////////////////////////////// double pstep = xstep * pressureMult; xp = left; for (int i=0, end=max-min; i<=end; ++i) { yp = bottom+1; painter.drawLine(xp, yp, xp, yp+6); if (i>0) { // skip the first mid tick painter.drawLine(xp-pstep/2, yp, xp-pstep/2, yp+4); } label = QString("%1").arg(i+minpressure); GetTextExtent(label, w, h); graph.renderText(label, xp-w/2, yp+h+4); xp+= pstep; } schema::Channel & ichan = schema::channel[ipap.code]; schema::Channel & echan = schema::channel[epap.code]; QPoint mouse=graph.graphView()->currentMousePos(); if (region.contains(mouse)) { float p = minpressure + (mouse.x() - left) / pstep; mouseOverKey = floor(p*pressureMult); float ipap_minutes = ipap.times[mouseOverKey] / 60.0; float epap_minutes = epap.times[mouseOverKey] / 60.0; QString str = QString("%1%2").arg(mouseOverKey / pressureMult,3,'f',1).arg(STR_UNIT_CMH2O)+"\n"; bool good = false; if (ipap_minutes > 0) { good = true; str += ichan.label()+": "+QString("%1 %2").arg(ipap_minutes,3,'f',1).arg(STR_UNIT_Minutes)+"\n"; } if (epap_minutes > 0) { good = true; str += echan.label()+": "+QString("%1 %2").arg(epap_minutes,3,'f',1).arg(STR_UNIT_Minutes)+"\n"; } if (good) { str+="\n"; int nc = ipap.chans.size(); for (int i=0;iqMax(0, min-1)) { s2 = double(ipap.times[qMax(0, min-1)]/60.0); if (s2 < 0) s2=0; } else s2 =0; double lastyp = bottom - (s2 * ystep); int tmax = qMin(ipap.times.size(), max); const auto & ipaptimes = ipap.times; for (int i=qMax(min,1); i0) { double evpeak = ipap.peakevents; double bot = bottom+1; double g = 1.0; double r = double(height+3) / (evpeak); if (r < h+4) { g = 2.0; r = double(height+3) / (evpeak/g); if (r < h+4) { g = 5.0; r = double(height+3) / (evpeak/g); if (r < h+4) { g = 20.0; //r = double(height+3) / (evpeak/g); } } } evpeak = ceil(evpeak/g)*g; r = double(height+3) / (evpeak / g); yp = bot; widest_YAxis+=2; for (double f=0.0; f<=evpeak; f+=g) { painter.setPen(Qt::black); painter.drawLine(left-widest_YAxis, bot, left-widest_YAxis-4, bot); painter.setPen(QColor(128,128,128,64)); // painter.drawLine(left, bot, left+width, bot); label = QString("%1").arg(f); GetTextExtent(label, w, h); graph.renderText(label, left-widest_YAxis-w-8, bot+h/2-2 ); bot -= r; } estep = double(height) / ipap.peakevents; for (const auto ch : ipap.chans) { //(ch != CPAP_AHI) && //if ((ch != CPAP_Hypopnea) && (ch != CPAP_Obstructive) && (ch != CPAP_ClearAirway) && (ch != CPAP_Apnea)) continue; schema::Channel & chan = schema::channel[ch]; QColor col = chan.defaultColor(); col.setAlpha(40); painter.setPen(col); painter.setPen(QPen(col, lineThickness)); xp = left; if (ipap.events.size()>qMax(min-1,0)) { s2 = ipap.events[ch][qMax(min-1,0)]; } else s2 = 0; lastyp = bottom - (s2 * estep); int tmax = qMin(ipap.events.size(), max); const auto & ipapev = ipap.events[ch]; for (int i=qMax(min,1); i0) { estep = double(height) / epap.peakevents; for (int k=0; k qMax(min,0)) { s2 = double(epaptimes[qMax(min,0)]/60.0); } else { s2 = 0; } xp=left, lastyp = bottom - (s2 * ystep); int tmax = qMin(epaptimes.size(), max); for (int i=qMax(min,1); icurrentMousePos(); float ypos = top; int titleWidth = graph.graphView()->titleWidth; int marginWidth = gYAxis::Margin; QString text = schema::channel[m_presChannel].label(); QRectF rec(titleWidth-4, ypos, marginWidth, hix); rec.moveRight(left - 4); // graph.renderText(text, rec, Qt::AlignRight | Qt::AlignVCenter); if (rec.contains(mouse)) { QString text = schema::channel[m_presChannel].description(); graph.ToolTip(text, mouse.x() + 10, mouse.y(), TT_AlignLeft); } int w,h; GetTextExtent(text, w,h); graph.renderText(text, (left-4) - w, ypos + hix/2.0 + float(h)/2.0); text = STR_UNIT_Minutes; rec = QRectF(titleWidth-4, ypos+hix, marginWidth, hix); rec.moveRight(left - 4); GetTextExtent(text, w,h); graph.renderText(text, (left-4) - w, ypos + hix + hix/2.0 + float(h)/2.0); // graph.renderText(text, rec, Qt::AlignRight | Qt::AlignVCenter); float xpos = left; for (it = times.begin(); it != times_end; ++it) { QString text = QString::number(it.key()); QString value = QString("%1").arg(float(it.value()) / 60.0, 5, 'f', 1); QRectF rec(xpos, top, pix-1, hix); GetTextExtent(text, w,h); painter.fillRect(rec, QColor("orange")); graph.renderText(text, xpos + pix/2 - w/2, top + hix /2 + h/2); GetTextExtent(value, w,h); // rec.moveTop(top + hix); graph.renderText(value, xpos + pix/2 - w/2, top + hix+ hix /2+ h/2); xpos += pix; } ypos += hix * 2; // left = rect.left(); auto eit; //auto ev_end = events.end(); auto vit; int row = 0; for (int i=0; i< numchans; ++i) { ChannelID code = chans.at(i); schema::Channel & chan = schema::channel[code]; if (!chan.enabled()) continue; schema::ChanType type = chan.type(); eit = events.find(code); xpos = left; auto eit_end = eit.value().end(); QString text = chan.label(); rec = QRectF(titleWidth, ypos, marginWidth, hix); rec.moveRight(xpos - 4); if (rec.contains(mouse)) { QString text = chan.fullname(); if (type == schema::SPAN) { text += "\n"+QObject::tr("(% of time)"); } graph.ToolTip(text, mouse.x() + 10, mouse.y(), TT_AlignLeft); } GetTextExtent(text, w,h); graph.renderText(text, (left-4) - w, ypos + hix/2.0 + float(h)/2.0); for (it = times.begin(), vit = eit.value().begin(); vit != eit_end; ++vit, ++it) { float minutes = float(it.value()) / 60.0; float value = vit.value(); QString fmt = "%1"; if (type != schema::SPAN) { //fmt = "%1"; value = (minutes > 0.000001) ? (value * 60.0) / minutes : 0; } else { //fmt = "%1%"; value = (minutes > 0.000001) ? (100/minutes) * (value / 60.0) : 0; } QRectF rec(xpos, ypos, pix-1, hix); if ((row & 1) == 0) { painter.fillRect(rec, QColor(245,245,255,240)); } text = QString(fmt).arg(value,5,'f',2); GetTextExtent(text, w,h); graph.renderText(text, xpos + pix/2 - w/2, ypos + hix /2 + h/2); // painter.drawText(rec, Qt::AlignCenter, QString(fmt).arg(value,5,'f',2)); xpos += pix; } ypos += hix; row++; } float maxmins = float(maxtime) / 60.0; float ymult = height / maxmins; row = 0; xpos = left ;//+ pix / 2; float y1, y2; it = times.begin(); float bottom = top+height; if (it != times_end) { QVector P; QVector tap; P.resize(26); tap.reserve(260); for (; it != times_end; ++it) { int p = it.key(); // No ASSERTS!!! (p < 255); float v = float(it.value()) / 60.0; P[p] = v; } for (int i=0;i<10;++i) { tap.append(0); } for (int i=1; i<24; ++i) { float p0 = P[i-1]; float p1 = P[i]; float p2 = P[i+1]; float p3 = P[i+2]; // Calculate Catmull-Rom Splines in between samples tap.append(P[i]); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.1))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.2))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.3))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.4))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.5))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.6))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.7))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.8))); tap.append(qMax(0.0f,CatmullRomSpline(p0,p1,p2,p3,0.9))); } tap.append(P[24]); tap.append(P[25]); painter.setPen(QPen(QColor(Qt::gray), 2)); float minutes = tap[35]; y1 = minutes * ymult; int tapsize = tap.size(); //xpos += pix / 10; for (int i=36; ieventlist.find(code); // Done already if no channel if (ei == sess->eventlist.end()) return; pressureMult = (sess->machine()->loaderName() == "PRS1") ? 2 : 5; // Loop through event lists for (const auto & EL : ei.value()) { gain = EL->gain(); // Don't bother with short sessions ELsize = EL->count(); if (ELsize < 1) continue; lasttime = 0; lastdata = 0; first = true; // Skip if outside of range if ((EL->first() > maxx) || (EL->last() < minx)) { continue; } // Scan through pressure samples for (int e = 0; e < ELsize; ++e) { if (m_quit) { m_done = true; return; } time = EL->time(e); data = floor(float(EL->raw(e)) * gain * pressureMult); // pressure times ten, so can look at .1 intervals in an integer if (data>=300) { qWarning() << "data >= 300 in RecalcMAP::updateTimes!"; return; } if ((time < minx) || first) { lasttime = time; lastdata = data; first = false; continue; } if (lastdata != data) { d1 = qMax(minx, lasttime); d2 = qMin(maxx, time); duration = (d2 - d1) / 1000L; key = lastdata; info.times[key] += duration; for (const auto & cod : info.chans) { schema::Channel & chan = schema::channel[cod]; if (chan.type() == schema::SPAN) { info.events[cod][key] += val = sess->rangeSum(cod, d1, d2); } else { info.events[cod][key] += val = sess->rangeCount(cod, d1, d2); } } lasttime = time; lastdata = data; } if (time > maxx) { break; } } if ((lasttime < maxx) || (lastdata == data)) { d1 = qMax(lasttime, minx); d2 = qMin(maxx, EL->last()); duration = (d2 - d1) / 1000L; key = lastdata; info.times[key] += duration; for (const auto & cod : info.chans) { schema::Channel & chan = schema::channel[cod]; if (chan.type() == schema::SPAN) { info.events[cod][key] += sess->rangeSum(cod, d1, d2); } else { info.events[cod][key] += sess->rangeCount(cod, d1, d2); } } } } } void PressureInfo::finishCalcs() { peaktime = peakevents = 0; min_pressure = max_pressure = 0; int val; for (int i=0, end=times.size(); i 0) { if (min_pressure == 0) { min_pressure = i; } max_pressure = i; } } //chans.push_front(CPAP_AHI); int size = events[CPAP_Obstructive].size(); /* events[CPAP_AHI].resize(size); auto OB = events.find(CPAP_Obstructive); auto HY = events.find(CPAP_Hypopnea); auto A = events.find(CPAP_Apnea); auto CA = events.find(CPAP_ClearAirway); for (int i = 0; i < size; i++) { val = 0; if (OB != events.end()) val += OB.value()[i]; if (HY != events.end()) val += HY.value()[i]; if (A != events.end()) val += A.value()[i]; if (CA != events.end()) val += CA.value()[i]; events[CPAP_AHI][i] = val; } */ for (int i = 0; i < size; i++) { for (const auto & cod : chans) { if ((cod == CPAP_AHI) || (schema::channel[cod].type() == schema::SPAN)) continue; val = events[cod][i]; peakevents = qMax(val, peakevents); } } } void RecalcMAP::run() { QMutexLocker locker(&map->mutex); map->m_recalculating = true; Day * day = map->m_day; if (!day) return; // Get the channels for specified Channel types QList chans = day->getSortedMachineChannels(schema::FLAG); chans.removeAll(CPAP_VSnore); chans.removeAll(CPAP_VSnore2); chans.removeAll(CPAP_FlowLimit); chans.removeAll(CPAP_RERA); // for (int i=0;ichannelExists(CPAP_IPAP)) ? CPAP_IPAP : CPAP_Pressure; ChannelID epapcode = (day->channelExists(CPAP_EPAP)) ? CPAP_EPAP : 0; qint64 minx, maxx; map->m_graph->graphView()->GetXBounds(minx, maxx); PressureInfo IPAP(ipapcode, minx, maxx), EPAP(epapcode, minx, maxx); IPAP.AddChannels(chans); EPAP.AddChannels(chans); //ChannelID code; // QList badchans; // for (int i=0 ; i < chans.size(); ++i) { // code = chans.at(i); // // if (!day->channelExists(code)) badchans.push_back(code); // } // for (int i=0; i < badchans.size(); ++i) { // code = badchans.at(i); // chans.removeAll(code); // } // int numchans = chans.size(); // // Zero the pressure counts // for (int i=map->m_minpressure; i <= map->m_maxpressure; i++) { // times[i] = 0; // for (int c = 0; c < numchans; ++c) { // code = chans.at(c); // events[code].insert(i, 0); // } // } for (const auto & sess : day->sessions) { updateTimes(EPAP, sess); updateTimes(IPAP, sess); if (m_quit) { m_done = true; return; } /* auto ei = sess->eventlist.find(ipapcode); if (ei == sess->eventlist.end()) continue; const auto & evec = ei.value(); int esize = evec.size(); for (int ei = 0; ei < esize; ++ei) { const EventList *EL = evec.at(ei); EventDataType gain = EL->gain(); quint32 ELsize = EL->count(); if (ELsize < 1) return; qint64 lasttime = 0; //EL->time(0); EventStoreType lastdata = 0; // EL->raw(0); bool first = true; if ((EL->first() > maxx) || (EL->last() < minx)) { continue; } for (quint32 e = 0; e < ELsize; ++e) { qint64 time = EL->time(e); EventStoreType data = EL->raw(e); if ((time < minx)) { lasttime = time; lastdata = data; first = false; goto skip; } if (first) { lasttime = time; lastdata = data; first = false; } if (lastdata != data) { qint64 d1 = qMax(minx, lasttime); qint64 d2 = qMin(maxx, time); int duration = (d2 - d1) / 1000L; EventStoreType key = floor(lastdata * gain); if (key <= 30) { times[key] += duration; for (int c = 0; c < chans.size(); ++c) { ChannelID code = chans.at(c); schema::Channel & chan = schema::channel[code]; if (chan.type() == schema::SPAN) { events[code][key] += sess->rangeSum(code, d1, d2); } else { events[code][key] += sess->rangeCount(code, d1, d2); } } } lasttime = time; lastdata = data; } if (time > maxx) { break; } skip: if (m_quit) { m_done = true; return; } } if (lasttime < maxx) { qint64 d1 = qMax(lasttime, minx); qint64 d2 = qMin(maxx, EL->last()); int duration = (d2 - d1) / 1000L; EventStoreType key = floor(lastdata * gain); if (key <= 30) { times[key] += duration; for (int c = 0; c < chans.size(); ++c) { ChannelID code = chans.at(c); schema::Channel & chan = schema::channel[code]; if (chan.type() == schema::SPAN) { events[code][key] += sess->rangeSum(code, d1, d2); } else { events[code][key] += sess->rangeCount(code, d1, d2); } } } } } */ } EPAP.finishCalcs(); IPAP.finishCalcs(); /* int maxtime = 0; QList trash; for (auto it=times.begin(), end=times.end(); it != end; ++it) { //EventStoreType key = it.key(); int value = it.value(); // if (value == 0) { // trash.append(key); // } else { maxtime = qMax(value, maxtime); // } } chans.push_front(CPAP_AHI); int maxevents = 0, val; for (int i = map->m_minpressure; i <= map->m_maxpressure; i++) { val = events[CPAP_Obstructive][i] + events[CPAP_Hypopnea][i] + events[CPAP_Apnea][i] + events[CPAP_ClearAirway][i]; events[CPAP_AHI].insert(i, val); // maxevents = qMax(val, maxevents); } for (int i = map->m_minpressure; i <= map->m_maxpressure; i++) { for (int j=0 ; j < chans.size(); ++j) { code = chans.at(j); if ((code == CPAP_AHI) || (schema::channel[code].type() == schema::SPAN)) continue; val = events[code][i]; maxevents = qMax(val, maxevents); } } // for (int i=0; i< trash.size(); ++i) { // EventStoreType key = trash.at(i); // times.remove(key); // for (auto eit=events.begin(), end=events.end(); eit != end; ++eit) { // eit.value().remove(key); // } // } */ map->timelock.lock(); // map->times = times; // map->events = events; map->epap = EPAP; map->ipap = IPAP; // map->chans = chans; // map->m_presChannel = ipapcode; map->timelock.unlock(); map->recalcFinished(); m_done = true; } void MinutesAtPressure::recalculate(gGraph * graph) { while (recalculating()) m_remap->quit(); m_remap = new RecalcMAP(this); m_remap->setAutoDelete(true); m_graph = graph; QThreadPool * tp = QThreadPool::globalInstance(); // tp->reserveThread(); if (graph->printing()) { m_remap->run(); } else { while(!tp->tryStart(m_remap)); m_lastmaxx = m_maxx; m_lastminx = m_minx; } // Start recalculating in another thread, organize a callback to redraw when done.. } void MinutesAtPressure::recalcFinished() { if (m_graph && !m_graph->printing()) { // Can't call this using standard timedRedraw function, we are in another thread, so have to use a throwaway timer QTimer::singleShot(0, m_graph->graphView(), SLOT(refreshTimeout())); } m_remap = nullptr; m_recalculating = false; // QThreadPool * tp = QThreadPool::globalInstance(); // tp->releaseThread(); } bool MinutesAtPressure::mouseMoveEvent(QMouseEvent *, gGraph *graph) { // int y = event->y() - m_rect.top(); // int x = event->x() - graph->graphView()->titleWidth; // double w = m_rect.width() - gYAxis::Margin; // double xmult = (graph->blockZoom() ? double(graph->rmax_x - graph->rmin_x) : //double(graph->max_x - graph->min_x)) / w; // double a = x - gYAxis::Margin; // if (a < 0) a = 0; // if (a > w) a = w; // double b = a * xmult; // double c= b + (graph->blockZoom() ? graph->rmin_x : graph->min_x); // graph->graphView()->setCurrentTime(c); if (graph) graph->timedRedraw(0); return false; } bool MinutesAtPressure::mousePressEvent(QMouseEvent *event, gGraph *graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } bool MinutesAtPressure::mouseReleaseEvent(QMouseEvent *event, gGraph *graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; }