Modified gLinePlot to support sublayers, so I could add a legend like summarychart.

Start of Session hiding ability
Intellipap fixes
This commit is contained in:
Mark Watkins 2011-12-27 23:21:10 +10:00
parent 8b4983a06f
commit d2381eac60
14 changed files with 498 additions and 298 deletions

View File

@ -15,6 +15,8 @@
#include "Graphs/gYAxis.h" #include "Graphs/gYAxis.h"
#include "Graphs/gFlagsLine.h" #include "Graphs/gFlagsLine.h"
#include "gLineChart.h"
extern MainWindow *mainwin; extern MainWindow *mainwin;
@ -1662,6 +1664,16 @@ short gGraph::marginRight() { return m_marginright; } //*m_graphview->printScale
short gGraph::marginTop() { return m_margintop; } //*m_graphview->printScaleY(); } short gGraph::marginTop() { return m_margintop; } //*m_graphview->printScaleY(); }
short gGraph::marginBottom() { return m_marginbottom; } //*m_graphview->printScaleY(); } short gGraph::marginBottom() { return m_marginbottom; } //*m_graphview->printScaleY(); }
Layer * gGraph::getLineChart()
{
gLineChart *lc;
for (int i=0;i<m_layers.size();i++) {
lc=dynamic_cast<gLineChart *>(m_layers[i]);
if (lc) return lc;
}
return NULL;
}
QPixmap gGraph::renderPixmap(int w, int h, bool printing) QPixmap gGraph::renderPixmap(int w, int h, bool printing)
{ {
@ -2020,6 +2032,7 @@ void gGraphView::scrollbarValueChanged(int val)
redraw(); // do this on a timer? redraw(); // do this on a timer?
} }
} }
void gGraphView::selectionTime() void gGraphView::selectionTime()
{ {
qint64 xx=m_maxx - m_minx; qint64 xx=m_maxx - m_minx;

View File

@ -610,6 +610,7 @@ public:
GLShortBuffer * stippled(); GLShortBuffer * stippled();
short left,right,top,bottom; // dirty magin hacks.. short left,right,top,bottom; // dirty magin hacks..
Layer * getLineChart();
QRect m_lastbounds; QRect m_lastbounds;
QTimer * timer; QTimer * timer;

View File

@ -15,6 +15,7 @@
gLineChart::gLineChart(ChannelID code,QColor col,bool square_plot, bool disable_accel) gLineChart::gLineChart(ChannelID code,QColor col,bool square_plot, bool disable_accel)
:Layer(code),m_square_plot(square_plot),m_disable_accel(disable_accel) :Layer(code),m_square_plot(square_plot),m_disable_accel(disable_accel)
{ {
addPlot(code,col,square_plot);
m_line_color=col; m_line_color=col;
m_report_empty=false; m_report_empty=false;
addGLBuf(lines=new GLShortBuffer(100000,GL_LINES)); addGLBuf(lines=new GLShortBuffer(100000,GL_LINES));
@ -27,10 +28,55 @@ gLineChart::~gLineChart()
//delete outlines; //delete outlines;
} }
bool gLineChart::isEmpty()
{
if (!m_day) return true;
for (int j=0;j<m_codes.size();j++) {
ChannelID code=m_codes[j];
for (int i=0;i<m_day->size();i++) {
Session *sess=m_day->getSessions()[i];
if (sess->channelExists(code))
return false;
}
}
return true;
}
void gLineChart::SetDay(Day *d) void gLineChart::SetDay(Day *d)
{ {
Layer::SetDay(d); // Layer::SetDay(d);
m_day=d;
m_minx=0,m_maxx=0;
m_miny=0,m_maxy=0;
if (!d) return;
qint64 t64;
EventDataType min=99999999,max=-999999999,tmp;
for (int j=0;j<m_codes.size();j++) {
ChannelID code=m_codes[j];
for (int i=0;i<d->size();i++) {
Session *sess=d->getSessions()[i];
tmp=sess->Min(code);
if (min > tmp) min=tmp;
tmp=sess->Max(code);
if (max < tmp) max=tmp;
t64=sess->first(code);
if (!m_minx || (m_minx > t64)) m_minx=t64;
t64=sess->last(code);
if (!m_maxx || (m_maxx < t64)) m_maxx=t64;
}
}
m_miny=min;
m_maxy=max;
//if (m_code==CPAP_Leak) { //if (m_code==CPAP_Leak) {
// subtract_offset=profile.cpap.[IntentionalLeak].toDouble(); // subtract_offset=profile.cpap.[IntentionalLeak].toDouble();
@ -131,326 +177,346 @@ void gLineChart::paint(gGraph & w,int left, int top, int width, int height)
QHash<ChannelID,QVector<EventList *> >::iterator ci; QHash<ChannelID,QVector<EventList *> >::iterator ci;
m_line_color=schema::channel[m_code].defaultColor(); //m_line_color=schema::channel[m_code].defaultColor();
for (int svi=0;svi<m_day->size();svi++) { int legendx=left+width;
if (!(*m_day)[svi]) {
qWarning() << "gLineChart::Plot() NULL Session Record.. This should not happen"; int codepoints;
continue; for (int gi=0;gi<m_codes.size();gi++) {
} ChannelID code=m_codes[gi];
schema::Channel ch=schema::channel[m_code]; m_line_color=m_colors[gi];
bool fndbetter=false;
for (QList<schema::Channel *>::iterator l=ch.m_links.begin();l!=ch.m_links.end();l++) { codepoints=0;
schema::Channel *c=*l; for (int svi=0;svi<m_day->size();svi++) {
ci=(*m_day)[svi]->eventlist.find(c->id()); if (!(*m_day)[svi]) {
if (ci!=(*m_day)[svi]->eventlist.end()) { qWarning() << "gLineChart::Plot() NULL Session Record.. This should not happen";
fndbetter=true; continue;
break;
} }
schema::Channel ch=schema::channel[code];
} bool fndbetter=false;
if (!fndbetter) { for (QList<schema::Channel *>::iterator l=ch.m_links.begin();l!=ch.m_links.end();l++) {
ci=(*m_day)[svi]->eventlist.find(m_code); schema::Channel *c=*l;
if (ci==(*m_day)[svi]->eventlist.end()) continue; ci=(*m_day)[svi]->eventlist.find(c->id());
} if (ci!=(*m_day)[svi]->eventlist.end()) {
fndbetter=true;
break;
QVector<EventList *> & evec=ci.value();
num_points=0;
for (int i=0;i<evec.size();i++)
num_points+=evec[i]->count();
total_points+=num_points;
const int num_averages=20; // Max n umber of samples taken from samples per pixel for better min/max values
for (int n=0;n<evec.size();n++) { // for each segment
EventList & el=*evec[n];
accel=(el.type()==EVL_Waveform); // Turn on acceleration if this is a waveform.
if (accel) {
sr=el.rate(); // Time distance between samples
if (sr<=0) {
qWarning() << "qLineChart::Plot() assert(sr>0)";
continue;
} }
} }
if (m_disable_accel) accel=false; if (!fndbetter) {
ci=(*m_day)[svi]->eventlist.find(code);
if (ci==(*m_day)[svi]->eventlist.end()) continue;
square_plot=m_square_plot;
if (accel || num_points>20000) { // Don't square plot if too many points or waveform
square_plot=false;
} }
int siz=evec[n]->count();
if (siz<=1) continue; // Don't bother drawing 1 point or less.
x0=el.time(0); QVector<EventList *> & evec=ci.value();
xL=el.time(siz-1); num_points=0;
for (int i=0;i<evec.size();i++)
num_points+=evec[i]->count();
total_points+=num_points;
codepoints+=num_points;
const int num_averages=20; // Max n umber of samples taken from samples per pixel for better min/max values
for (int n=0;n<evec.size();n++) { // for each segment
EventList & el=*evec[n];
if (maxx<x0) continue; accel=(el.type()==EVL_Waveform); // Turn on acceleration if this is a waveform.
if (xL<minx) continue; if (accel) {
sr=el.rate(); // Time distance between samples
if (x0>xL) { if (sr<=0) {
if (siz==2) { // this happens on CPAP qWarning() << "qLineChart::Plot() assert(sr>0)";
quint32 t=el.getTime()[0]; continue;
el.getTime()[0]=el.getTime()[1]; }
el.getTime()[1]=t;
EventStoreType d=el.getData()[0];
el.getData()[0]=el.getData()[1];
el.getData()[1]=d;
} else {
qDebug() << "Reversed order sample fed to gLineChart - ignored.";
continue;
//assert(x1<x2);
} }
} if (m_disable_accel) accel=false;
if (accel) {
//x1=el.time(1);
double XR=xx/sr;
double Z1=MAX(x0,minx); square_plot=m_square_plot;
double Z2=MIN(xL,maxx); if (accel || num_points>20000) { // Don't square plot if too many points or waveform
double ZD=Z2-Z1; square_plot=false;
double ZR=ZD/sr;
double ZQ=ZR/XR;
double ZW=ZR/(width*ZQ);
visible_points+=ZR*ZQ;
if (accel && n>0) {
sam=1;
} }
if (ZW<num_averages) {
sam=1; int siz=evec[n]->count();
accel=false; if (siz<=1) continue; // Don't bother drawing 1 point or less.
} else {
sam=ZW/num_averages; x0=el.time(0);
if (sam<1) { xL=el.time(siz-1);
if (maxx<x0) continue;
if (xL<minx) continue;
if (x0>xL) {
if (siz==2) { // this happens on CPAP
quint32 t=el.getTime()[0];
el.getTime()[0]=el.getTime()[1];
el.getTime()[1]=t;
EventStoreType d=el.getData()[0];
el.getData()[0]=el.getData()[1];
el.getData()[1]=d;
} else {
qDebug() << "Reversed order sample fed to gLineChart - ignored.";
continue;
//assert(x1<x2);
}
}
if (accel) {
//x1=el.time(1);
double XR=xx/sr;
double Z1=MAX(x0,minx);
double Z2=MIN(xL,maxx);
double ZD=Z2-Z1;
double ZR=ZD/sr;
double ZQ=ZR/XR;
double ZW=ZR/(width*ZQ);
visible_points+=ZR*ZQ;
if (accel && n>0) {
sam=1;
}
if (ZW<num_averages) {
sam=1; sam=1;
accel=false; accel=false;
} else {
sam=ZW/num_averages;
if (sam<1) {
sam=1;
accel=false;
}
} }
// Prepare the min max y values if we still are accelerating this plot
if (accel) {
for (int i=0;i<width;i++) {
m_drawlist[i].setX(height);
m_drawlist[i].setY(0);
}
minz=width;
maxz=0;
}
total_visible+=visible_points;
} else {
sam=1;
} }
// Prepare the min max y values if we still are accelerating this plot
if (accel) { // these calculations over estimate
for (int i=0;i<width;i++) { // The Z? values are much more accurate
m_drawlist[i].setX(height);
m_drawlist[i].setY(0); idx=0;
}
minz=width; if (el.type()==EVL_Waveform) {
maxz=0; // We can skip data previous to minx if this is a waveform
if (minx>x0) {
double j=minx-x0; // == starting min of first sample in this segment
idx=(j/sr);
//idx/=(sam*num_averages);
//idx*=(sam*num_averages);
// Loose the precision
idx+=sam-(idx % sam);
} // else just start from the beginning
} }
total_visible+=visible_points;
} else {
sam=1;
}
// these calculations over estimate int xst=left+1;
// The Z? values are much more accurate int yst=top+height+1;
idx=0; double time;
EventDataType data;
EventDataType gain=el.gain();
EventDataType nmult=ymult*gain;
EventDataType ymin=EventDataType(miny)/gain;
if (el.type()==EVL_Waveform) { const QVector<EventStoreType> & dat=el.getData();
// We can skip data previous to minx if this is a waveform const QVector<quint32> & tim=el.getTime();
if (minx>x0) { done=false;
double j=minx-x0; // == starting min of first sample in this segment first=true;
idx=(j/sr);
//idx/=(sam*num_averages);
//idx*=(sam*num_averages);
// Loose the precision
idx+=sam-(idx % sam);
} // else just start from the beginning if (!accel) {
} lines->setSize(1.5);
} else lines->setSize(1);
bool firstpx=true;
if (el.type()==EVL_Waveform) { // Waveform Plot
if (idx>sam) idx-=sam;
time=el.time(idx);
double rate=double(sr)*double(sam);
int xst=left+1; if (accel) {
int yst=top+height+1; //////////////////////////////////////////////////////////////////
// Accelerated Waveform Plot
//////////////////////////////////////////////////////////////////
for (int i=idx;i<siz;i+=sam) {
time+=rate;
//time=el.time(i);
//if (time < minx)
// continue; // Skip stuff before the start of our data window
double time; //data=el.data(i);
EventDataType data; data=dat[i];//*gain;
EventDataType gain=el.gain(); px=((time - minx) * xmult); // Scale the time scale X to pixel scale X
EventDataType nmult=ymult*gain; py=((data - ymin) * nmult); // Same for Y scale
EventDataType ymin=EventDataType(miny)/gain;
const QVector<EventStoreType> & dat=el.getData(); // In accel mode, each pixel has a min/max Y value.
const QVector<quint32> & tim=el.getTime(); // m_drawlist's index is the pixel index for the X pixel axis.
done=false; int z=round(px); // Hmmm... round may screw this up.
first=true; if (z<minz) minz=z; // minz=First pixel
if (z>maxz) maxz=z; // maxz=Last pixel
if (minz<0) {
qDebug() << "gLineChart::Plot() minz<0 should never happen!! minz =" << minz;
minz=0;
}
if (maxz>max_drawlist_size) {
qDebug() << "gLineChart::Plot() maxz>max_drawlist_size!!!! maxz = " << maxz << " max_drawlist_size =" << max_drawlist_size;
maxz=max_drawlist_size;
}
if (!accel) { // Update the Y pixel bounds.
lines->setSize(1.5); if (py<m_drawlist[z].x()) m_drawlist[z].setX(py);
} else lines->setSize(1); if (py>m_drawlist[z].y()) m_drawlist[z].setY(py);
bool firstpx=true;
if (el.type()==EVL_Waveform) { // Waveform Plot
if (idx>sam) idx-=sam;
time=el.time(idx);
double rate=double(sr)*double(sam);
if (accel) { if (time > maxx) {
////////////////////////////////////////////////////////////////// done=true; // Let this iteration finish.. (This point will be in far clipping)
// Accelerated Waveform Plot break;
////////////////////////////////////////////////////////////////// }
for (int i=idx;i<siz;i+=sam) {
time+=rate;
//time=el.time(i);
//if (time < minx)
// continue; // Skip stuff before the start of our data window
//data=el.data(i);
data=dat[i];//*gain;
px=((time - minx) * xmult); // Scale the time scale X to pixel scale X
py=((data - ymin) * nmult); // Same for Y scale
// In accel mode, each pixel has a min/max Y value.
// m_drawlist's index is the pixel index for the X pixel axis.
int z=round(px); // Hmmm... round may screw this up.
if (z<minz) minz=z; // minz=First pixel
if (z>maxz) maxz=z; // maxz=Last pixel
if (minz<0) {
qDebug() << "gLineChart::Plot() minz<0 should never happen!! minz =" << minz;
minz=0;
} }
if (maxz>max_drawlist_size) { // Plot compressed accelerated vertex list
qDebug() << "gLineChart::Plot() maxz>max_drawlist_size!!!! maxz = " << maxz << " max_drawlist_size =" << max_drawlist_size; if (maxz>width) {
maxz=max_drawlist_size; //qDebug() << "gLineChart::Plot() maxz exceeded graph width" << "maxz = " << maxz << "width =" << width;
maxz=width;
}
float ax1,ay1;
for (int i=minz;i<maxz;i++) {
// ax1=(m_drawlist[i-1].x()+m_drawlist[i].x()+m_drawlist[i+1].x())/3.0;
// ay1=(m_drawlist[i-1].y()+m_drawlist[i].y()+m_drawlist[i+1].y())/3.0;
ax1=m_drawlist[i].x();
ay1=m_drawlist[i].y();
lines->add(xst+i,yst-ax1,xst+i,yst-ay1,m_line_color);
if (lines->full()) break;
} }
// Update the Y pixel bounds. } else { // Zoomed in Waveform
if (py<m_drawlist[z].x()) m_drawlist[z].setX(py); //////////////////////////////////////////////////////////////////
if (py>m_drawlist[z].y()) m_drawlist[z].setY(py); // Normal Waveform Plot
//////////////////////////////////////////////////////////////////
if (time > maxx) { if (idx>sam) {
done=true; // Let this iteration finish.. (This point will be in far clipping) idx-=sam;
break; time=el.time(idx);
//double rate=double(sr)*double(sam);
} }
} for (int i=idx;i<siz;i+=sam) {
// Plot compressed accelerated vertex list time+=rate;
if (maxz>width) { //if (time < minx)
//qDebug() << "gLineChart::Plot() maxz exceeded graph width" << "maxz = " << maxz << "width =" << width; // continue; // Skip stuff before the start of our data window
maxz=width; data=dat[i];//el.data(i);
}
float ax1,ay1;
for (int i=minz;i<maxz;i++) {
// ax1=(m_drawlist[i-1].x()+m_drawlist[i].x()+m_drawlist[i+1].x())/3.0;
// ay1=(m_drawlist[i-1].y()+m_drawlist[i].y()+m_drawlist[i+1].y())/3.0;
ax1=m_drawlist[i].x();
ay1=m_drawlist[i].y();
lines->add(xst+i,yst-ax1,xst+i,yst-ay1);
if (lines->full()) break; px=xst+((time - minx) * xmult); // Scale the time scale X to pixel scale X
} py=yst-((data - ymin) * nmult); // Same for Y scale, with precomputed gain
} else { // Zoomed in Waveform if (firstpx) {
////////////////////////////////////////////////////////////////// lastpx=px;
// Normal Waveform Plot lastpy=py;
////////////////////////////////////////////////////////////////// firstpx=false;
if (idx>sam) { continue;
idx-=sam; }
time=el.time(idx); lines->add(lastpx,lastpy,px,py,m_line_color);
//double rate=double(sr)*double(sam);
}
for (int i=idx;i<siz;i+=sam) {
time+=rate;
//if (time < minx)
// continue; // Skip stuff before the start of our data window
data=dat[i];//el.data(i);
px=xst+((time - minx) * xmult); // Scale the time scale X to pixel scale X if (lines->full()) {
py=yst-((data - ymin) * nmult); // Same for Y scale, with precomputed gain done=true;
break;
if (firstpx) { }
if (time > maxx) {
//done=true; // Let this iteration finish.. (This point will be in far clipping)
break;
}
lastpx=px; lastpx=px;
lastpy=py; lastpy=py;
firstpx=false;
continue;
} }
lines->add(lastpx,lastpy,px,py); }
if (lines->full()) { } else {
done=true; //////////////////////////////////////////////////////////////////
break; // Standard events/zoomed in Plot
//////////////////////////////////////////////////////////////////
first=true;
double start=el.first();
if (siz==2) {
time=start+tim[0];
data=dat[0]*gain;
data-=subtract_offset;
lastpy=yst-((data - miny) * ymult); // Same for Y scale with precomputed gain
lastpx=xst+((time - minx) * xmult); // Scale the time scale X to pixel scale X
if (lastpx<xst-1) lastpx=xst-1;
EventDataType data2=(dat[1]*gain)-subtract_offset;
qint64 time2=start+tim[1];
py=yst-((data2 - miny) * ymult);
px=xst+((time2 - minx) * xmult);
if (px>xst+width) px=xst+width;
lines->add(lastpx,lastpy,px,py,m_line_color);
} else
for (int i=0;i<siz;i++) {
time=start+tim[i];
if (first) {
if (num_points>15 && (time < minx)) continue; // Skip stuff before the start of our data window
first=false;
if (i>0) i--; // Start with the previous sample (which will be in clipping area)
time=start+tim[i];
} }
data=dat[i]*gain; //
data-=subtract_offset;
//data=el.data(i); // raw access is faster
px=xst+((time - minx) * xmult); // Scale the time scale X to pixel scale X
//py=yst+((data - ymin) * nmult); // Same for Y scale with precomputed gain
py=yst-((data - miny) * ymult); // Same for Y scale with precomputed gain
//if (px<left) px=left;
//if (px>left+width) px=left+width;
if (firstpx) {
firstpx=false;
} else {
if (square_plot) {
lines->add(lastpx,lastpy,px,lastpy,px,lastpy,px,py,m_line_color);
} else {
lines->add(lastpx,lastpy,px,py,m_line_color);
}
//lines->add(px,py,m_line_color);
if (lines->full()) {
done=true;
break;
}
}
lastpx=px;
lastpy=py;
//if (lastpx>start_px+width) done=true;
if (time > maxx) { if (time > maxx) {
//done=true; // Let this iteration finish.. (This point will be in far clipping) //done=true; // Let this iteration finish.. (This point will be in far clipping)
break; break;
} }
lastpx=px;
lastpy=py;
} }
} }
} else { if (done) break;
//////////////////////////////////////////////////////////////////
// Standard events/zoomed in Plot
//////////////////////////////////////////////////////////////////
first=true;
double start=el.first();
if (siz==2) {
time=start+tim[0];
data=dat[0]*gain;
data-=subtract_offset;
lastpy=yst-((data - miny) * ymult); // Same for Y scale with precomputed gain
lastpx=xst+((time - minx) * xmult); // Scale the time scale X to pixel scale X
if (lastpx<xst-1) lastpx=xst-1;
EventDataType data2=(dat[1]*gain)-subtract_offset;
qint64 time2=start+tim[1];
py=yst-((data2 - miny) * ymult);
px=xst+((time2 - minx) * xmult);
if (px>xst+width) px=xst+width;
lines->add(lastpx,lastpy,px,py);
} else
for (int i=0;i<siz;i++) {
time=start+tim[i];
if (first) {
if (num_points>15 && (time < minx)) continue; // Skip stuff before the start of our data window
first=false;
if (i>0) i--; // Start with the previous sample (which will be in clipping area)
time=start+tim[i];
}
data=dat[i]*gain; //
data-=subtract_offset;
//data=el.data(i); // raw access is faster
px=xst+((time - minx) * xmult); // Scale the time scale X to pixel scale X
//py=yst+((data - ymin) * nmult); // Same for Y scale with precomputed gain
py=yst-((data - miny) * ymult); // Same for Y scale with precomputed gain
//if (px<left) px=left;
//if (px>left+width) px=left+width;
if (firstpx) {
firstpx=false;
} else {
if (square_plot) {
lines->add(lastpx,lastpy,px,lastpy,px,lastpy,px,py);
} else {
lines->add(lastpx,lastpy,px,py);
}
//lines->add(px,py,m_line_color);
if (lines->full()) {
done=true;
break;
}
}
lastpx=px;
lastpy=py;
//if (lastpx>start_px+width) done=true;
if (time > maxx) {
//done=true; // Let this iteration finish.. (This point will be in far clipping)
break;
}
}
} }
if (done) break;
} }
if ((codepoints>0)) { //(m_codes.size()>1) &&
// Draw Legends for plots..
QString text=schema::channel[code].label();
int wid,hi;
GetTextExtent(text,wid,hi);
legendx-=wid;
w.renderText(text,legendx,top-4);
int bw=hi;
legendx-=hi/2;
w.quads()->add(legendx-bw,top-4,legendx,top-4,legendx,top-bw,legendx-bw,top-bw,m_line_color);
legendx-=hi+hi/2;
}
} }
if (!total_points) { // No Data? if (!total_points) { // No Data?
if (m_report_empty) { if (m_report_empty) {

View File

@ -98,6 +98,18 @@ class gLineChart:public Layer
//! \brief Returns Maximum Y-axis value for this layer //! \brief Returns Maximum Y-axis value for this layer
virtual EventDataType Maxy(); virtual EventDataType Maxy();
//! \brief Returns true if all subplots contain no data
virtual bool isEmpty();
//! \brief Add Subplot 'code'. Note the first one is added in the constructor.
void addPlot(ChannelID code, QColor color, bool square) { m_codes.push_back(code); m_colors.push_back(color); m_enabled[code]=true; m_square.push_back(square); }
//! \brief Returns true of the subplot 'code' is enabled.
bool plotEnabled(ChannelID code) { if ((m_enabled.contains(code)) && m_enabled[code]) return true; else return false; }
//! \brief Enable or Disable the subplot identified by code.
void setPlotEnabled(ChannelID code, bool b) { m_enabled[code]=b; }
protected: protected:
bool m_report_empty; bool m_report_empty;
bool m_square_plot; bool m_square_plot;
@ -114,6 +126,11 @@ protected:
QPoint m_drawlist[max_drawlist_size]; QPoint m_drawlist[max_drawlist_size];
int subtract_offset; int subtract_offset;
QVector<ChannelID> m_codes;
QVector<QColor> m_colors;
QVector<bool> m_square;
QHash<ChannelID,bool> m_enabled;
}; };
#endif // GLINECHART_H #endif // GLINECHART_H

View File

@ -24,6 +24,13 @@ MachineType Day::machine_type()
{ {
return machine->GetType(); return machine->GetType();
} }
Session *Day::find(SessionID sessid) {
for (int i=0;i<size();i++) {
if (sessions[i]->session()==sessid)
return sessions[i];
}
return NULL;
}
void Day::AddSession(Session *s) void Day::AddSession(Session *s)
{ {

View File

@ -121,6 +121,8 @@ public:
//! \brief Finds and returns the index of a session, otherwise -1 if it's not there //! \brief Finds and returns the index of a session, otherwise -1 if it's not there
int find(Session * sess) { return sessions.indexOf(sess); } int find(Session * sess) { return sessions.indexOf(sess); }
Session *find(SessionID sessid);
//! \brief Returns the number of Sessions in this day record //! \brief Returns the number of Sessions in this day record
int size() { return sessions.size(); } int size() { return sessions.size(); }

View File

@ -319,6 +319,27 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
//} //}
quint64 first=qint64(sid)*1000L; quint64 first=qint64(sid)*1000L;
quint64 last=qint64(SessionEnd[i])*1000L; quint64 last=qint64(SessionEnd[i])*1000L;
EventDataType max=sess->Max(CPAP_IPAP);
EventDataType min=sess->Min(CPAP_EPAP);
EventDataType pres=sess->Min(CPAP_Pressure);
if (max==min) {
sess->settings[CPAP_Mode]=(int)MODE_CPAP;
sess->settings[CPAP_PressureMin]=min;
sess->settings[CPAP_PressureMax]=min;
} else {
sess->settings[CPAP_Mode]=(int)MODE_APAP;
sess->settings[CPAP_PressureMin]=min;
sess->settings[CPAP_PressureMax]=max;
}
sess->eventlist.erase(sess->eventlist.find(CPAP_IPAP));
sess->eventlist.erase(sess->eventlist.find(CPAP_EPAP));
sess->m_min.erase(sess->m_min.find(CPAP_EPAP));
sess->m_max.erase(sess->m_max.find(CPAP_EPAP));
if (pres<min) {
sess->settings[CPAP_RampPressure]=pres;
}
//quint64 len=last-first; //quint64 len=last-first;
//if (len>0) { //if (len>0) {
//if (!sess->first()) { //if (!sess->first()) {

View File

@ -414,7 +414,7 @@ SleepStage::~SleepStage()
} }
ChannelID NoChannel; ChannelID NoChannel, SESSION_ENABLED;
ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_Pressure, CPAP_PS, CPAP_Mode, CPAP_AHI, ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_Pressure, CPAP_PS, CPAP_Mode, CPAP_AHI,
CPAP_PressureMin, CPAP_PressureMax, CPAP_RampTime, CPAP_RampPressure, CPAP_Obstructive, CPAP_Hypopnea, CPAP_PressureMin, CPAP_PressureMax, CPAP_RampTime, CPAP_RampPressure, CPAP_Obstructive, CPAP_Hypopnea,
CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_VSnore, CPAP_VSnore2, CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_VSnore, CPAP_VSnore2,

View File

@ -77,7 +77,7 @@ enum MCDataType
{ MC_bool=0, MC_int, MC_long, MC_float, MC_double, MC_string, MC_datetime }; { MC_bool=0, MC_int, MC_long, MC_float, MC_double, MC_string, MC_datetime };
extern ChannelID NoChannel; extern ChannelID NoChannel,SESSION_ENABLED;
extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_Pressure, CPAP_PS, CPAP_Mode, CPAP_AHI, extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_Pressure, CPAP_PS, CPAP_Mode, CPAP_AHI,
CPAP_PressureMin, CPAP_PressureMax, CPAP_RampTime, CPAP_RampPressure, CPAP_Obstructive, CPAP_Hypopnea, CPAP_PressureMin, CPAP_PressureMax, CPAP_RampTime, CPAP_RampPressure, CPAP_Obstructive, CPAP_Hypopnea,
CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_VSnore, CPAP_VSnore2, CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_VSnore, CPAP_VSnore2,

View File

@ -19,6 +19,7 @@ namespace schema {
ChannelList channel; ChannelList channel;
Channel EmptyChannel; Channel EmptyChannel;
Channel SessionEnabledChannel;
QHash<QString,ChanType> ChanTypes; QHash<QString,ChanType> ChanTypes;
QHash<QString,DataType> DataTypes; QHash<QString,DataType> DataTypes;
@ -32,7 +33,9 @@ void init()
schema_initialized=true; schema_initialized=true;
EmptyChannel=Channel(0,DATA,DAY,"Empty","Empty Channel","",""); EmptyChannel=Channel(0,DATA,DAY,"Empty","Empty Channel","","");
SessionEnabledChannel=Channel(1,DATA,DAY,"Enabled","Session Enabled","","");
SESSION_ENABLED=1;
ChanTypes["data"]=DATA; ChanTypes["data"]=DATA;
//Types["waveform"]=WAVEFORM; //Types["waveform"]=WAVEFORM;
ChanTypes["setting"]=SETTING; ChanTypes["setting"]=SETTING;

View File

@ -217,11 +217,12 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
bool square=PROFILE.appearance->squareWavePlots(); bool square=PROFILE.appearance->squareWavePlots();
PRD->AddLayer(AddCPAP(new gLineChart(CPAP_EPAP,Qt::blue,square))); gLineChart *pc=new gLineChart(CPAP_Pressure,QColor("dark green"),square);
PRD->AddLayer(AddCPAP(new gLineChart(CPAP_IPAPLo,Qt::darkRed,square))); PRD->AddLayer(AddCPAP(pc));
PRD->AddLayer(AddCPAP(new gLineChart(CPAP_IPAP,Qt::red,square))); pc->addPlot(CPAP_EPAP,Qt::blue,square);
PRD->AddLayer(AddCPAP(new gLineChart(CPAP_IPAPHi,Qt::darkRed,square))); pc->addPlot(CPAP_IPAPLo,Qt::darkRed,square);
PRD->AddLayer(AddCPAP(new gLineChart(CPAP_Pressure,QColor("dark green"),square))); pc->addPlot(CPAP_IPAP,Qt::red,square);
pc->addPlot(CPAP_IPAPHi,Qt::darkRed,square);
if (PROFILE.general->calculateRDI()) { if (PROFILE.general->calculateRDI()) {
AHI->AddLayer(AddCPAP(new AHIChart(QColor("#37a24b")))); AHI->AddLayer(AddCPAP(new AHIChart(QColor("#37a24b"))));
@ -229,9 +230,12 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
AHI->AddLayer(AddCPAP(new gLineChart(CPAP_AHI,QColor("light green"),square))); AHI->AddLayer(AddCPAP(new gLineChart(CPAP_AHI,QColor("light green"),square)));
} }
LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_LeakTotal,Qt::darkYellow,square))); gLineChart *lc=new gLineChart(CPAP_LeakTotal,Qt::darkYellow,square);
LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_Leak,Qt::darkMagenta,square))); lc->addPlot(CPAP_Leak,Qt::darkMagenta,square);
LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_MaxLeak,Qt::darkRed,square))); lc->addPlot(CPAP_MaxLeak,Qt::darkRed,square);
LEAK->AddLayer(AddCPAP(lc));
//LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_Leak,Qt::darkMagenta,square)));
//LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_MaxLeak,Qt::darkRed,square)));
SNORE->AddLayer(AddCPAP(new gLineChart(CPAP_Snore,Qt::darkGray,true))); SNORE->AddLayer(AddCPAP(new gLineChart(CPAP_Snore,Qt::darkGray,true)));
PTB->AddLayer(AddCPAP(new gLineChart(CPAP_PTB,Qt::gray,square))); PTB->AddLayer(AddCPAP(new gLineChart(CPAP_PTB,Qt::gray,square)));
@ -346,7 +350,22 @@ void Daily::Link_clicked(const QUrl &url)
QString data=url.toString().section("=",1); QString data=url.toString().section("=",1);
int sid=data.toInt(); int sid=data.toInt();
Day *day=NULL; Day *day=NULL;
if (code=="cpap") { if (code=="togglecpapsession") {
day=PROFILE.GetDay(previous_date,MT_CPAP);
Session *sess=day->find(sid);
if (!sess)
return;
bool b;
if (sess->settings.contains(SESSION_ENABLED)) b=false;
else b=!sess->settings[SESSION_ENABLED].toBool();
sess->settings[SESSION_ENABLED]=b;
sess->SetChanged(true);
day->machine->Save();
GraphView->ResetBounds();
// reload day
return;
} else if (code=="cpap") {
day=PROFILE.GetDay(previous_date,MT_CPAP); day=PROFILE.GetDay(previous_date,MT_CPAP);
} else if (code=="oxi") { } else if (code=="oxi") {
day=PROFILE.GetDay(previous_date,MT_OXIMETER); day=PROFILE.GetDay(previous_date,MT_OXIMETER);
@ -924,8 +943,9 @@ void Daily::Load(QDate date)
QDateTime fd,ld; QDateTime fd,ld;
bool corrupted_waveform=false; bool corrupted_waveform=false;
QString tooltip; QString tooltip;
html+=QString("<tr><td align=left><b>%1</b></td><td align=center><b>%2</b></td><td align=center><b>%3</b></td><td align=center><b>%4</b></td></tr>") html+=QString("<tr><td align=left><b>%1</b></td><td><b>%2</b></td><td align=center><b>%3</b></td><td align=center><b>%4</b></td><td align=center><b>%5</b></td></tr>")
.arg(tr("SessionID")) .arg(tr("SessionID"))
.arg(tr("Show"))
.arg(tr("Date")) .arg(tr("Date"))
.arg(tr("Start")) .arg(tr("Start"))
.arg(tr("End")); .arg(tr("End"));
@ -943,10 +963,20 @@ void Daily::Load(QDate date)
// tooltip needs to lookup language.. :-/ // tooltip needs to lookup language.. :-/
if ((i!=(*s)->settings.end()) && i.value().toBool()) corrupted_waveform=true; if ((i!=(*s)->settings.end()) && i.value().toBool()) corrupted_waveform=true;
tmp.sprintf(("<tr><td align=left><a href='cpap=%i' title='"+tooltip+"'>%08i</a></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(),(*s)->session()); Session *sess=*s;
html+=tmp; if (!sess->settings.contains(SESSION_ENABLED)) {
sess->settings[SESSION_ENABLED]=true;
}
bool b=sess->settings[SESSION_ENABLED].toBool();
html+=QString("<tr><td align=left><a href='cpap=%1' title='%2'>%3</a></td><td><a href='togglecpapsession=%1'><img src='qrc:/icons/toggle-%4-us.svg' width=24px></a></td><td align=center>%5</td><td align=center>%6</td><td align=center>%7</td></tr>")
.arg((*s)->session())
.arg(tooltip)
.arg((*s)->session(),8,10,QChar('0'))
.arg((b ? "on" : "off"))
.arg(fd.date().toString(Qt::SystemLocaleShortDate))
.arg(fd.toString("HH:mm"))
.arg(ld.toString("HH:mm"));
} }
//if (oxi) html+="<tr><td colspan=4><hr></td></tr>";
} }
if (oxi) { if (oxi) {
html+=QString("<tr><td align=left colspan=4><i>%1</i></td></tr>").arg(tr("Oximetry Sessions")); html+=QString("<tr><td align=left colspan=4><i>%1</i></td></tr>").arg(tr("Oximetry Sessions"));
@ -960,10 +990,23 @@ void Daily::Load(QDate date)
QHash<ChannelID,QVariant>::iterator i=(*s)->settings.find(CPAP_BrokenWaveform); QHash<ChannelID,QVariant>::iterator i=(*s)->settings.find(CPAP_BrokenWaveform);
tooltip=oxi->machine->GetClass()+" "+tr("Oximeter")+" "+QString().sprintf("%2ih,&nbsp;%2im,&nbsp;%2is",h,m,s1); tooltip=oxi->machine->GetClass()+" "+tr("Oximeter")+" "+QString().sprintf("%2ih,&nbsp;%2im,&nbsp;%2is",h,m,s1);
Session *sess=*s;
if (!sess->settings.contains(SESSION_ENABLED)) {
sess->settings[SESSION_ENABLED]=true;
}
bool b=sess->settings[SESSION_ENABLED].toBool();
if ((i!=(*s)->settings.end()) && i.value().toBool()) corrupted_waveform=true; if ((i!=(*s)->settings.end()) && i.value().toBool()) corrupted_waveform=true;
tmp.sprintf(("<tr><td align=left><a href='oxi=%i' title='"+tooltip+"'>%08i</a></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(),(*s)->session()); html+=QString("<tr><td align=left><a href='oxi=%1' title='%2'>%3</a></td><td><a href='toggleoxisession=%1'><img src='qrc:/icons/toggle-%4-us.svg' width=24px></a></td><td align=center>%5</td><td align=center>%6</td><td align=center>%7</td></tr>")
html+=tmp; .arg((*s)->session())
.arg(tooltip)
.arg((*s)->session(),8,10,QChar('0'))
.arg((b ? "on" : "off"))
.arg(fd.date().toString(Qt::SystemLocaleShortDate))
.arg(fd.toString("HH:mm"))
.arg(ld.toString("HH:mm"));
//tmp.sprintf(("<tr><td align=left><a href='oxi=%i' title='"+tooltip+"'>%08i</a></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(),(*s)->session());
//html+=tmp;
} }
} }
if (corrupted_waveform) { if (corrupted_waveform) {

View File

@ -10,6 +10,7 @@
#include <QResource> #include <QResource>
#include <QProgressBar> #include <QProgressBar>
#include <QWebHistory> #include <QWebHistory>
#include <QWebFrame>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDesktopServices> #include <QDesktopServices>
#include <QNetworkReply> #include <QNetworkReply>
@ -145,6 +146,7 @@ MainWindow::MainWindow(QWidget *parent) :
daily->graphView()->redraw(); daily->graphView()->redraw();
ui->recordsBox->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); ui->recordsBox->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
ui->summaryView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
ui->webView->page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks); ui->webView->page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
ui->toolBox->setStyleSheet( ui->toolBox->setStyleSheet(
"QToolBox::tab {" "QToolBox::tab {"
@ -375,6 +377,11 @@ QString htmlHeader()
"a:link,a:visited { color: '#000020'; text-decoration: none; font-weight: bold;}" "a:link,a:visited { color: '#000020'; text-decoration: none; font-weight: bold;}"
"a:hover { background-color: inherit; color: red; text-decoration:none; font-weight: bold; }" "a:hover { background-color: inherit; color: red; text-decoration:none; font-weight: bold; }"
"</style>" "</style>"
"<script type='text/javascript'>"
"function ChangeColor(tableRow, highLight)"
"{ tableRow.style.backgroundColor = highLight; }"
"function Go(url) { window.location=url; }"
"</script>"
"</head>" "</head>"
"<body leftmargin=0 topmargin=0 rightmargin=0>" "<body leftmargin=0 topmargin=0 rightmargin=0>"
"<div align=center><table cellpadding=3 cellspacing=0 border=0>" "<div align=center><table cellpadding=3 cellspacing=0 border=0>"
@ -949,20 +956,22 @@ void MainWindow::on_summaryButton_clicked()
RXChange rx=rxchange.at(i); RXChange rx=rxchange.at(i);
QString color; QString color;
if (rx.highlight==1) { if (rx.highlight==1) {
color=" bgcolor='#c0ffc0'"; color="#c0ffc0";
} else if (rx.highlight==2) { } else if (rx.highlight==2) {
color=" bgcolor='#e0ffe0'"; color="#e0ffe0";
} else if (rx.highlight==3) { } else if (rx.highlight==3) {
color=" bgcolor='#ffe0e0'"; color="#ffe0e0";
} else if (rx.highlight==4) { } else if (rx.highlight==4) {
color=" bgcolor='#ffc0c0'"; color="#ffc0c0";
} else color=""; } else color="";
if (cpapmode>=MODE_BIPAP) { if (cpapmode>=MODE_BIPAP) {
extratxt=QString("<td>%1</td><td>%2</td><td>%3</td>").arg(rx.max,0,'f',2).arg(rx.per1,0,'f',2).arg(rx.per2,0,'f',2); extratxt=QString("<td>%1</td><td>%2</td><td>%3</td>").arg(rx.max,0,'f',2).arg(rx.per1,0,'f',2).arg(rx.per2,0,'f',2);
} else if (cpapmode>MODE_CPAP) { } else if (cpapmode>MODE_CPAP) {
extratxt=QString("<td>%1</td><td>%2</td>").arg(rx.max,0,'f',2).arg(rx.per1,0,'f',2); extratxt=QString("<td>%1</td><td>%2</td>").arg(rx.max,0,'f',2).arg(rx.per1,0,'f',2);
} else extratxt=""; } else extratxt="";
html+=QString("<tr"+color+"><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td>%7</tr>") html+=QString("<tr bgcolor='"+color+"' onmouseover='ChangeColor(this, \"#dddddd\");' onmouseout='ChangeColor(this, \""+color+"\");' onclick='tabwidget.setCurrentIndex(3); print \"%1 %2\";'><td>%3</td><td>%4</td><td>%5</td><td>%6</td><td>%7</td><td>%8</td>%9</tr>")
.arg(rx.first.toString(Qt::ISODate))
.arg(rx.last.toString(Qt::ISODate))
.arg(rx.first.toString(Qt::SystemLocaleShortDate)) .arg(rx.first.toString(Qt::SystemLocaleShortDate))
.arg(rx.last.toString(Qt::SystemLocaleShortDate)) .arg(rx.last.toString(Qt::SystemLocaleShortDate))
.arg(rx.days) .arg(rx.days)
@ -1005,6 +1014,8 @@ void MainWindow::on_summaryButton_clicked()
} }
updateFavourites(); updateFavourites();
html+=htmlFooter(); html+=htmlFooter();
QWebFrame *frame=ui->summaryView->page()->currentFrame();
frame->addToJavaScriptWindowObject("mainwin",this);
ui->summaryView->setHtml(html); ui->summaryView->setHtml(html);
// QString file="qrc:/docs/index.html"; // QString file="qrc:/docs/index.html";
// QUrl url(file); // QUrl url(file);
@ -2129,3 +2140,15 @@ void MainWindow::on_tabWidget_currentChanged(int index)
oximetry->graphView()->selectionTime(); oximetry->graphView()->selectionTime();
} }
} }
void MainWindow::on_summaryView_linkClicked(const QUrl &arg1)
{
qDebug() << arg1;
on_recordsBox_linkClicked(arg1);
}
void MainWindow::on_summaryView_urlChanged(const QUrl &arg1)
{
// qDebug() << arg1;
// on_recordsBox_linkClicked(arg1);
}

View File

@ -269,6 +269,10 @@ private slots:
void LinkHovered(const QString & link, const QString & title, const QString & textContent); void LinkHovered(const QString & link, const QString & title, const QString & textContent);
void on_tabWidget_currentChanged(int index); void on_tabWidget_currentChanged(int index);
void on_summaryView_linkClicked(const QUrl &arg1);
void on_summaryView_urlChanged(const QUrl &arg1);
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;

View File

@ -24,7 +24,7 @@
<item> <item>
<widget class="QStackedWidget" name="stackedWidget"> <widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>4</number>
</property> </property>
<widget class="QWidget" name="welcomePage"> <widget class="QWidget" name="welcomePage">
<layout class="QVBoxLayout" name="verticalLayout_8"> <layout class="QVBoxLayout" name="verticalLayout_8">
@ -73,7 +73,7 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;It's intended as merely a data viewer, and not a substitute for competent medical guidance from your Doctor. &lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;It's intended as merely a data viewer, and not a substitute for competent medical guidance from your Doctor. &lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This software has been released freely under the &lt;a href=&quot;http://www.gnu.org/copyleft/gpl.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;GNU Public License&lt;/span&gt;&lt;/a&gt;, and comes with no warranty, and without ANY claims to fitness for any purpose.&lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This software has been released freely under the &lt;a href=&quot;qrc:/LICENSE.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;GNU Public License&lt;/span&gt;&lt;/a&gt;, and comes with no warranty, and without ANY claims to fitness for any purpose.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Accuracy of any data displayed is not and can not be guaranteed. &lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Accuracy of any data displayed is not and can not be guaranteed. &lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
@ -702,7 +702,7 @@ p, li { white-space: pre-wrap; }
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_17"> <widget class="QLabel" name="label_17">
<property name="text"> <property name="text">
<string>Name</string> <string>Doctors Name</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -719,7 +719,7 @@ p, li { white-space: pre-wrap; }
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_23"> <widget class="QLabel" name="label_23">
<property name="text"> <property name="text">
<string>Practice</string> <string>Practice Name</string>
</property> </property>
</widget> </widget>
</item> </item>