Add error message via a tooltip when invalid date is entered ifor Overview date range

This commit is contained in:
LoudSnorer 2022-04-17 19:34:42 -04:00
parent 4e1c8a2f52
commit 272d630c9e
4 changed files with 273 additions and 59 deletions

View File

@ -11,10 +11,12 @@
#ifdef DEBUG_FUNCTIONS
#include <QRegularExpression>
#define DEBUG qDebug()<<QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__
#define DEBUGF qDebug()<<QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__ << __func__
#define DEBUGTF qDebug()<<QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz") << QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__ << __func__
#define DEBUGF qDebug()<< QString("%1[%2]%3").arg( QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) ).arg(__LINE__).arg(__func__)
#define O( XX ) " " #XX ":" << XX
#define Q( XX ) << #XX ":" << XX
#define R( XX )
#define OO( XX , YY ) " " #XX ":" << YY
#define NAME( id) schema::channel[ id ].label()
#define DATE( XX ) QDateTime::fromMSecsSinceEpoch(XX).toString("dd MMM yyyy")
@ -139,10 +141,8 @@ void gToolTip::display(QString text, int x, int y, ToolTipAlignment align, int t
if (timer->isActive()) {
timer->stop();
}
timer->setSingleShot(true);
timer->start(timeout);
m_invalidate = true;
}
void gToolTip::cancel()
@ -151,17 +151,14 @@ void gToolTip::cancel()
timer->stop();
}
void gToolTip::paint(QPainter &painter) //actually paints it.
QRect gToolTip::calculateRect(QPainter &painter)
{
if (!m_visible) { return; }
int x = m_pos.x();
int y = m_pos.y();
QRect rect(x, y, 0, 0);
painter.setFont(*defaultfont);
painter.setFont(*m_font);
rect = painter.boundingRect(rect, Qt::AlignCenter, m_text);
int w = rect.width() + m_spacer * 2;
@ -201,19 +198,25 @@ void gToolTip::paint(QPainter &painter) //actually paints it.
rect.setTop(rect.top()-bot);
rect.setBottom(m_graphview->height());
}
return rect;
}
void gToolTip::paint(QPainter &painter) //actually paints it.
{
if (!m_visible) { return; }
QRect a_rect=calculateRect(painter);
QBrush brush(QColor(255, 255, 128, 230));
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
painter.setPen(QColor(0, 0, 0, 255));
painter.drawRoundedRect(rect, 5, 5);
painter.drawRoundedRect(a_rect, 5, 5);
painter.setBrush(Qt::black);
painter.setFont(*defaultfont);
painter.setFont(*m_font);
painter.drawText(rect, Qt::AlignCenter, m_text);
painter.drawText(a_rect, Qt::AlignCenter, m_text);
}
void gToolTip::timerDone()
@ -223,6 +226,88 @@ void gToolTip::timerDone()
m_graphview->resetMouse();
}
/* Parent tool tip
Allow the parent (overview or daily) to add tooltip or short messages to the user.
The basic problem is that the parent does not know the current dimensions of the graph view.
the parent does have knowledge of the location of fixed widgets which makes it possible to
locate tool tips in an appropiate location.
*/
gParentToolTip::gParentToolTip(gGraphView *graphview)
: gToolTip(graphview) {
m_parent_visible=false;
}
gParentToolTip::~gParentToolTip() {
}
void gParentToolTip::display(gGraphView* gv,QString text, int verticalOffset, int alignOffset, ToolTipAlignment align , int timeout ,QFont *font ) {
m_text=text;
m_verticalOffset=verticalOffset;
m_alignOffset=alignOffset;
m_alignment=align;
m_timeout=timeout;
m_font=font;
m_parent_visible=true;
gv->timedRedraw(0);
};
QRect gParentToolTip::calculateRect(QPainter& painter ) {
QRect rect(0, 0, 0, 0);
painter.setFont(*m_font);
rect = painter.boundingRect(rect, m_alignment, m_text);
// update space arround text
int space=2*m_spacer;
rect.setHeight(space+rect.height());
rect.setWidth(space+rect.width());
rect.moveTo(m_alignOffset,m_height-(m_verticalOffset+rect.height()));
// move rect accounding to alignment. default is left.
if (m_alignment == TT_AlignRight) {
// move rect left by width of rect. if <0 use 0;
rect.moveLeft(rect.left()-rect.width());
} else if (m_alignment == TT_AlignCenter) {
// left by 1/2 width of rect. if < 0 then use 0
rect.moveLeft(rect.left()-rect.width()/2);
}
if (rect.top()<0) {rect.setTop(0);};
if (rect.left()<0) {rect.setLeft(0);};
return rect;
}
void gParentToolTip::paint(QPainter &painter,int width,int height) {
if (!m_parent_visible) {return ;};
m_width=width;
m_height=height;
gToolTip::display(m_text, 0, 0,m_alignment, m_timeout);
gToolTip::paint(painter);
};
void gParentToolTip::timerDone() {
gToolTip::timerDone();
if (m_parent_visible) {
m_graphview->timedRedraw(0);
}
m_parent_visible=false;
};
void gParentToolTip::cancel() {
gToolTip::cancel();
m_parent_visible=false;
};
bool gParentToolTip::visible() {
return gToolTip::visible() && m_parent_visible;
};
#ifdef ENABLE_THREADED_DRAWING
gThread::gThread(gGraphView *g)
@ -359,6 +444,7 @@ gGraphView::gGraphView(QWidget *parent, gGraphView *shared, QWidget *caller)
masterlock = new QSemaphore(m_idealthreads);
#endif
m_tooltip = new gToolTip(this);
m_parent_tooltip = new gParentToolTip(this);
/*for (int i=0;i<m_idealthreads;i++) {
gThread * gt=new gThread(this);
m_threads.push_back(gt);
@ -634,6 +720,7 @@ gGraphView::~gGraphView()
}
delete m_tooltip;
delete m_parent_tooltip;
m_graphs.clear();
}
@ -1499,6 +1586,7 @@ void gGraphView::paintGL()
AppSetting->usePixmapCaching() ? DrawTextQueCached(painter) :DrawTextQue(painter);
m_tooltip->paint(painter);
m_parent_tooltip->paint(painter,width(), height() );
#ifdef DEBUG_EFFICIENCY
const int rs = 20;

View File

@ -240,10 +240,12 @@ class gToolTip : public QObject
virtual void paint(QPainter &paint); //actually paints it.
//! \brief Close the tooltip early.
void cancel();
virtual void cancel();
//! \brief Returns true if the tooltip is currently visible
bool visible() { return m_visible; }
virtual bool visible() { return m_visible; }
virtual QRect calculateRect(QPainter &painter);
protected:
gGraphView *m_graphview;
@ -254,13 +256,35 @@ class gToolTip : public QObject
bool m_visible;
int m_spacer;
QImage m_image;
bool m_invalidate;
ToolTipAlignment m_alignment;
QFont* m_font=defaultfont;
protected slots:
//! \brief Timeout to hide tooltip, and redraw without it.
void timerDone();
virtual void timerDone();
};
class gParentToolTip : public gToolTip
{
Q_OBJECT
public:
gParentToolTip(gGraphView *graphview);
~gParentToolTip();
virtual void display(gGraphView* gv,QString text,int verticalOffset, int alignOffset , ToolTipAlignment align = TT_AlignCenter, int timeout = 0,QFont *font = defaultfont) ;
virtual void cancel();
virtual bool visible();
virtual QRect calculateRect(QPainter &painter);
virtual void paint(QPainter &paint,int width,int height) ;
private:
int m_verticalOffset;
int m_alignOffset;
int m_width;
int m_height;
bool m_parent_visible;
int m_timeout;
protected slots:
virtual void timerDone();
};
struct SelectionHistoryItem {
@ -425,6 +449,7 @@ class gGraphView
gGraph *m_selected_graph;
gToolTip *m_tooltip;
gParentToolTip *m_parent_tooltip;
QTimer *timer;
//! \brief Add the Text information to the Text Drawing Queue (called by gGraphs renderText method)

View File

@ -7,16 +7,19 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
#define NEWSTUFF
#define xDEBUG_FUNCTIONS
#ifdef DEBUG_FUNCTIONS
#include <QRegularExpression>
#define DEBUGQ qDebug()
#define DEBUGL qDebug()<<QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__
#define DEBUGF qDebug()<<QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__ << __func__
#define DEBUGTF qDebug()<<QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz") << QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__ << __func__
#define DEBUGF qDebug()<< QString("%1[%2]%3").arg( QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) ).arg(__LINE__).arg(__func__)
#define DEBUGT qDebug()<<QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz")
#define DEBUGTF qDebug()<<QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz") << QString(basename( __FILE__)).remove(QRegularExpression("\\..*$")) << __LINE__ << __func__
#define O( XX ) " " #XX ":" << XX
#define Q( XX ) << #XX ":" << XX
#define R( XX )
#define OO( XX , YY ) " " #XX ":" << YY
#define NAME( id) schema::channel[ id ].label()
#define DATE( XX ) QDateTime::fromMSecsSinceEpoch(XX).toString("dd MMM yyyy")
@ -74,7 +77,7 @@ Overview::Overview(QWidget *parent, gGraphView *shared) :
// Set Date controls locale to 4 digit years
QLocale locale = QLocale::system();
QString shortformat = locale.dateFormat(QLocale::ShortFormat);
shortformat = locale.dateFormat(QLocale::ShortFormat);
if (!shortformat.toLower().contains("yyyy")) {
shortformat.replace("yy", "yyyy");
@ -173,6 +176,7 @@ Overview::Overview(QWidget *parent, gGraphView *shared) :
GraphView->LoadSettings("Overview"); //no trans
GraphView->setEmptyImage(QPixmap(":/icons/logo-md.png"));
dateErrorDisplay = new DateErrorDisplay(this);
connect(ui->dateStart->calendarWidget(), SIGNAL(currentPageChanged(int, int)), this, SLOT(dateStart_currentPageChanged(int, int)));
connect(ui->dateEnd->calendarWidget(), SIGNAL(currentPageChanged(int, int)), this, SLOT(dateEnd_currentPageChanged(int, int)));
@ -196,6 +200,11 @@ Overview::~Overview()
GraphView->SaveSettings("Overview");//no trans
delete ui;
delete dateErrorDisplay;
delete icon_on ;
delete icon_off ;
delete icon_up_down ;
delete icon_warning ;
}
void Overview::ResetFont()
@ -235,12 +244,10 @@ void Overview::on_summaryChartEmpty(gSummaryChart*sc,qint64 firstI,qint64 lastI,
if (empty) {
// on next range change allow empty flag to be recalculated
chartsEmpty.insert(sc,graph);
//DEBUGF << graph->name() << "Chart is Empty" << "Range" << convertTimeToDate(firstI) << convertTimeToDate(lastI);
} else {
// The chart has some entry with data.
chartsEmpty.remove(sc);
updateGraphCombo();
//DEBUGF << graph->name() << "Chart is enabled with range:" << convertTimeToDate(firstI) << "==>" << convertTimeToDate(lastI) ;
}
Q_UNUSED(firstI);
Q_UNUSED(lastI);
@ -322,11 +329,8 @@ void Overview::CreateAllGraphs() {
G->AddLayer(sc);
chartsToBeMonitored.insert(sc,G);
}
if (sc== nullptr) {
//DEBUGF << "Channel" << name << "type" << chan->type() << "machine type" << chan->machtype() << "IGNORED";
} else {
if (sc!= nullptr) {
sc ->reCalculate();
//DEBUGF << "Channel" << name << "type" << chan->type() << "machine type" << chan->machtype() << OO(Empty,sc->isEmpty());
}
} // if showInOverview()
} // for chit
@ -549,12 +553,6 @@ void Overview::on_XBoundsChanged(qint64 start,qint64 end)
// Only occurs when custom mode is switched to/from a latest mode. custom mode to/from last week.
// All other displays expand the existing range.
// reset all empty flags to not empty
if (displayStartDate>maxRangeEndDate) {
//DEBUGF << "Two ranges" O(displayStartDate) <<">" << O(maxRangeEndDate);
}
if (minRangeStartDate>displayEndDate) {
//DEBUGF << "Two ranges" O(minRangeStartDate) <<">" << O(displayEndDate);
}
largerRange=true;
chartsEmpty = QHash<gSummaryChart*, gGraph*>( chartsToBeMonitored );
minRangeStartDate = displayStartDate;
@ -562,24 +560,14 @@ void Overview::on_XBoundsChanged(qint64 start,qint64 end)
} else {
// new range overlaps with old range
if (displayStartDate<minRangeStartDate) {
//DEBUGF << "Start lower" <<O(minRangeStartDate)<< ">" <<O(displayStartDate);
largerRange=true;
minRangeStartDate = displayStartDate;
}
if (displayEndDate>maxRangeEndDate) {
//DEBUGF << "End Higher" <<O(maxRangeEndDate)<< "<" <<O(displayEndDate);
largerRange=true;
maxRangeEndDate = displayEndDate;
}
}
if (!largerRange) {
if (displayStartDate<minRangeStartDate ) {
//DEBUGF << "ERROR" <<O(minRangeStartDate)<< "==" <<O(displayStartDate);
}
if (displayEndDate>maxRangeEndDate) {
//DEBUGF << "ERROR" <<O(maxRangeEndDate)<< "==" <<O(displayEndDate);
}
}
if (largerRange) {
for (auto it= chartsEmpty.begin();it!=chartsEmpty.end();it++) {
@ -624,6 +612,10 @@ void Overview::dateEnd_currentPageChanged(int year, int month)
void Overview::on_dateEnd_dateChanged(const QDate &date)
{
if (date<uiStartDate) {
dateErrorDisplay->error(false,date);
return;
}
QDate d2(date);
if (customMode) {
p_profile->general->setCustomOverviewRangeEnd(d2);
@ -636,6 +628,11 @@ void Overview::on_dateEnd_dateChanged(const QDate &date)
void Overview::on_dateStart_dateChanged(const QDate &date)
{
if (date>uiEndDate) {
// change date back to last date.
dateErrorDisplay->error(true,date);
return;
}
QDate d1(date);
if (customMode) {
p_profile->general->setCustomOverviewRangeStart(d1);
@ -654,6 +651,7 @@ void Overview::on_zoomButton_clicked()
on_rangeCombo_activated(p_profile->general->lastOverviewRange()); // type of range in last use
}
void Overview::ResetGraphLayout()
{
GraphView->resetLayout();
@ -732,7 +730,7 @@ void Overview::on_rangeCombo_activated(int index)
// Ensure that all summary files are available and update version numbers if required
int size = start.daysTo(end);
qDebug() << "Overview range combo from" << start << "to" << end << "with" << size << "days";
// qDebug() << "Overview range combo from" << start << "to" << end << "with" << size << "days";
QDate dateback = end;
CProgressBar * progress = new CProgressBar (QObject::tr("Loading summaries"), mainwin, size);
for (int i=1; i < size; ++i) {
@ -754,12 +752,101 @@ void Overview::on_rangeCombo_activated(int index)
setRange(start, end);
}
DateErrorDisplay::DateErrorDisplay (Overview* overview)
: m_overview(overview)
{
m_visible=false;
m_timer = new QTimer();
connect(m_timer, SIGNAL(timeout()),this, SLOT(timerDone()));
};
DateErrorDisplay::~DateErrorDisplay() {
disconnect(m_timer, SIGNAL(timeout()),this, SLOT(timerDone()));
delete m_timer;
};
void DateErrorDisplay::cancel() {
m_visible=false;
m_timer->stop();
};
void DateErrorDisplay::timerDone() {
m_visible=false;
m_overview->resetUiDates();
m_overview->graphView()->m_parent_tooltip->cancel();
};
int Overview::calculatePixels(bool startDate,ToolTipAlignment& align) {
// Center error message over start and end dates combined.
// Other allignement were tested but this is the best for this problem.
Q_UNUSED(startDate);
int space=4;
align=TT_AlignCenter;
return ((4*space) + ui->label_3->width() + ui->rangeCombo->width() + ui->dateStartLabel->width() +ui->dateStart->width() );
}
void DateErrorDisplay::error(bool startDate,const QDate& dateEntered) {
m_startDate =m_overview->uiStartDate;
m_endDate =m_overview->uiEndDate;
ToolTipAlignment align=TT_AlignCenter;
QString dateFormatted =dateEntered.toString(m_overview->shortformat);
QString txt = QString(tr("ERROR\nThe start date MUST be before the end date"));
txt.append("\n");
if (startDate) {
txt.append(tr("The entered start date %1 is after the end date %2").arg(dateFormatted).arg(m_endDate.toString(m_overview->shortformat)));
txt.append(tr("\nHint: Change the end date first"));
} else {
txt.append(tr("The entered end date %1 ").arg(dateFormatted));
txt.append(tr("is before the start date %1").arg(m_startDate.toString(m_overview->shortformat)));
txt.append(tr("\nHint: Change the start date first"));
}
gGraphView* gv=m_overview->graphView();
int pixelsFromLeft = m_overview->calculatePixels(startDate,align);
int pixelsAboveBottom = 0;
int warningDurationMS =4000;
int resetUiDatesTimerDelayMS =1000;
//QFont* font=mediumfont;
QFont* font=defaultfont;
gv->m_parent_tooltip->display(gv,txt,pixelsAboveBottom,pixelsFromLeft, align, warningDurationMS , font);
m_timer->setInterval( resetUiDatesTimerDelayMS );
m_timer->setSingleShot(true);
m_timer->start();
};
void Overview::resetUiDates() {
ui->dateStart->blockSignals(true);
ui->dateStart->setMinimumDate(p_profile->FirstDay()); // first and last dates for ANY machine type
ui->dateStart->setMaximumDate(p_profile->LastDay());
ui->dateStart->setDate(uiStartDate);
ui->dateStart->blockSignals(false);
ui->dateEnd->blockSignals(true);
ui->dateEnd->setMinimumDate(p_profile->FirstDay()); // first and last dates for ANY machine type
ui->dateEnd->setMaximumDate(p_profile->LastDay());
ui->dateEnd->setDate(uiEndDate);
ui->dateEnd->blockSignals(false);
}
// Saves dates in UI, clicks zoom button, and updates combo box
// 1. Updates the dates in the start / end date boxs
// 2. optionally also changes display range for graphs.
void Overview::setRange(QDate& start, QDate& end, bool updateGraphs/*zoom*/)
{
if (start>end) {
// this is an ERROR and shold NEVER occur.
return;
}
// first setting of the date (setDate) will cause pageChanged to be executed.
// The pageChanged processing requires access to the other ui's date.
// so save them to memory before the first call.
@ -777,21 +864,7 @@ void Overview::setRange(QDate& start, QDate& end, bool updateGraphs/*zoom*/)
samePage=nextSamePage;
}
ui->dateEnd->blockSignals(true);
ui->dateStart->blockSignals(true);
// Calling these methods for the first time trigger pageChange actions.
ui->dateEnd->setDate(end);
ui->dateEnd->setMinimumDate(start);
ui->dateStart->setMinimumDate(p_profile->FirstDay()); // first and last dates for ANY machine type
ui->dateEnd->setMaximumDate(p_profile->LastDay());
ui->dateStart->setDate(start);
ui->dateStart->setMaximumDate(end);
ui->dateEnd->blockSignals(false);
ui->dateStart->blockSignals(false);
resetUiDates();
if (updateGraphs) SetXBounds(uiStartDate,uiEndDate);
updateGraphCombo();
}

View File

@ -16,6 +16,7 @@
#endif
#include <QHBoxLayout>
#include <QDateEdit>
#include <QTimer>
#include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include "Graphs/gSummaryChart.h"
@ -26,6 +27,27 @@ class Overview;
}
class Report;
class Overview;
class DateErrorDisplay:QObject {
Q_OBJECT
public:
DateErrorDisplay (Overview* overview) ;
~DateErrorDisplay() ;
bool visible() {return m_visible;};
void cancel();
void error(bool startDate,const QDate& date);
protected:
private:
QTimer* m_timer;
bool m_visible=false;
Overview* m_overview;
QDate m_startDate;
QDate m_endDate;
private slots:
void timerDone();
};
enum YTickerType { YT_Number, YT_Time, YT_Weight };
@ -35,6 +57,7 @@ enum YTickerType { YT_Number, YT_Time, YT_Weight };
*/
class Overview : public QWidget
{
friend class DateErrorDisplay;
Q_OBJECT
public:
@ -103,8 +126,6 @@ class Overview : public QWidget
//! \brief Updates the calendar highlighting when changing to a new month
void dateEnd_currentPageChanged(int year, int month);
//void on_printDailyButton_clicked();
void on_rangeCombo_activated(int index);
void on_graphCombo_activated(int index);
@ -143,6 +164,10 @@ class Overview : public QWidget
void disconnectgSummaryCharts() ;
void SetXBounds(qint64 minx, qint64 maxx, short group = 0, bool refresh = true);
void SetXBounds(QDate & start, QDate& end, short group =0 , bool refresh = true);
void resetUiDates();
DateErrorDisplay* dateErrorDisplay;
int calculatePixels(bool startDate,ToolTipAlignment& align);
QString shortformat;
// Start and of dates of the current graph display
QDate displayStartDate;
@ -166,4 +191,7 @@ class Overview : public QWidget
};
#endif // OVERVIEW_H