QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsdatetimeedit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdatetimeedit.cpp
3  --------------------------------------
4  Date : 08.2014
5  Copyright : (C) 2014 Denis Rouzaud
6  Email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QAction>
17 #include <QCalendarWidget>
18 #include <QLineEdit>
19 #include <QMouseEvent>
20 #include <QStyle>
21 #include <QStyleOptionSpinBox>
22 
23 
24 #include "qgsdatetimeedit.h"
25 
26 #include "qgsapplication.h"
27 #include "qgslogger.h"
28 
29 
30 
32  : QgsDateTimeEdit( QDateTime(), QVariant::DateTime, parent )
33 {
34 
35 }
36 
38 QgsDateTimeEdit::QgsDateTimeEdit( const QVariant &var, QVariant::Type parserType, QWidget *parent )
39  : QDateTimeEdit( var, parserType, parent )
40  , mNullRepresentation( QgsApplication::nullRepresentation() )
41 {
42  QIcon clearIcon = QgsApplication::getThemeIcon( "/mIconClearText.svg" );
43  mClearAction = new QAction( clearIcon, tr( "clear" ), this );
44  mClearAction->setCheckable( false );
45  lineEdit()->addAction( mClearAction, QLineEdit::TrailingPosition );
46  mClearAction->setVisible( mAllowNull );
47  connect( mClearAction, &QAction::triggered, this, &QgsDateTimeEdit::clear );
48 
49  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
50 
51  // enable calendar widget by default so it's already created
52  setCalendarPopup( true );
53 
54  setMinimumEditDateTime();
55 
56  // init with current time so mIsNull is properly initialized
57  QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
58 }
60 
61 void QgsDateTimeEdit::setAllowNull( bool allowNull )
62 {
63  mAllowNull = allowNull;
64  mClearAction->setVisible( mAllowNull && ( !mIsNull || mIsEmpty ) );
65 }
66 
67 
69 {
70  if ( mAllowNull )
71  {
72  displayCurrentDate();
73 
74  // Check if it's really changed or crash, see GH #29937
75  if ( ! dateTime().isNull() )
76  {
77  changed( QDateTime() );
78  }
79 
80  // emit signal of QDateTime::dateTimeChanged with an invalid date
81  // anyway, using parent's signal should be avoided
82  // If you consequently connect parent's dateTimeChanged signal
83  // and call dateTime() afterwards there is no warranty to
84  // have a proper NULL value handling
85  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
86  emit dateTimeChanged( QDateTime() );
87  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
88  }
89 }
90 
92 {
93  mClearAction->setVisible( mAllowNull );
94  mIsEmpty = true;
95 }
96 
97 void QgsDateTimeEdit::mousePressEvent( QMouseEvent *event )
98 {
99  // catch mouse press on the button (when the current value is null)
100  // in non-calendar mode: modify the date so it leads to showing current date (don't bother about time)
101  // in calendar mode: be sure NULL is displayed when needed and show page of current date in calendar widget
102 
103  bool updateCalendar = false;
104 
105  if ( mIsNull )
106  {
107  QStyle::SubControl control;
108  if ( calendarPopup() )
109  {
110  QStyleOptionComboBox optCombo;
111  optCombo.init( this );
112  optCombo.editable = true;
113  optCombo.subControls = QStyle::SC_All;
114  control = style()->hitTestComplexControl( QStyle::CC_ComboBox, &optCombo, event->pos(), this );
115 
116  if ( control == QStyle::SC_ComboBoxArrow && calendarWidget() )
117  {
118  mCurrentPressEvent = true;
119  // ensure the line edit still displays NULL
120  updateCalendar = true;
121  displayNull( updateCalendar );
122  mCurrentPressEvent = false;
123  }
124  }
125  else
126  {
127  QStyleOptionSpinBox opt;
128  this->initStyleOption( &opt );
129  control = style()->hitTestComplexControl( QStyle::CC_SpinBox, &opt, event->pos(), this );
130 
131  if ( control == QStyle::SC_SpinBoxDown || control == QStyle::SC_SpinBoxUp )
132  {
133  mCurrentPressEvent = true;
134  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
135  resetBeforeChange( control == QStyle::SC_SpinBoxDown ? -1 : 1 );
136  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
137  mCurrentPressEvent = false;
138  }
139  }
140  }
141 
142  QDateTimeEdit::mousePressEvent( event );
143 
144  if ( updateCalendar )
145  {
146  // set calendar page to current date to avoid going to minimal date page when value is null
147  calendarWidget()->setCurrentPage( QDate::currentDate().year(), QDate::currentDate().month() );
148  }
149 }
150 
151 void QgsDateTimeEdit::focusOutEvent( QFocusEvent *event )
152 {
153  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
154  {
155  QAbstractSpinBox::focusOutEvent( event );
156  if ( lineEdit()->text() != mNullRepresentation )
157  {
158  displayNull();
159  }
160  emit editingFinished();
161  }
162  else
163  {
164  QDateTimeEdit::focusOutEvent( event );
165  }
166 }
167 
168 void QgsDateTimeEdit::focusInEvent( QFocusEvent *event )
169 {
170  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
171  {
172  QAbstractSpinBox::focusInEvent( event );
173 
174  displayCurrentDate();
175  }
176  else
177  {
178  QDateTimeEdit::focusInEvent( event );
179  }
180 }
181 
182 void QgsDateTimeEdit::wheelEvent( QWheelEvent *event )
183 {
184  // dateTime might have been set to minimum in calendar mode
185  if ( mAllowNull && mIsNull )
186  {
187  resetBeforeChange( -event->delta() );
188  }
189  QDateTimeEdit::wheelEvent( event );
190 }
191 
192 void QgsDateTimeEdit::showEvent( QShowEvent *event )
193 {
194  QDateTimeEdit::showEvent( event );
195  if ( mAllowNull && mIsNull &&
196  lineEdit()->text() != mNullRepresentation )
197  {
198  displayNull();
199  }
200 }
201 
203 void QgsDateTimeEdit::changed( const QVariant &dateTime )
204 {
205  mIsEmpty = false;
206  bool isNull = dateTime.isNull();
207  if ( isNull != mIsNull )
208  {
209  mIsNull = isNull;
210  if ( mIsNull )
211  {
212  if ( mOriginalStyleSheet.isNull() )
213  {
214  mOriginalStyleSheet = lineEdit()->styleSheet();
215  }
216  lineEdit()->setStyleSheet( QStringLiteral( "QLineEdit { font-style: italic; color: grey; }" ) );
217  }
218  else
219  {
220  lineEdit()->setStyleSheet( mOriginalStyleSheet );
221  }
222  }
223 
224  mClearAction->setVisible( mAllowNull && !mIsNull );
225  if ( !mBlockChangedSignal )
227 }
229 
231 {
232  return mNullRepresentation;
233 }
234 
235 void QgsDateTimeEdit::setNullRepresentation( const QString &nullRepresentation )
236 {
237  mNullRepresentation = nullRepresentation;
238  if ( mIsNull )
239  {
240  lineEdit()->setText( mNullRepresentation );
241  }
242 }
243 
244 void QgsDateTimeEdit::displayNull( bool updateCalendar )
245 {
246  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
247  if ( updateCalendar )
248  {
249  // set current time to minimum date time to avoid having
250  // a date selected in calendar widget
251  QDateTimeEdit::setDateTime( minimumDateTime() );
252  }
253  lineEdit()->setCursorPosition( lineEdit()->text().length() );
254  lineEdit()->setText( mNullRepresentation );
255  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
256 }
257 
258 void QgsDateTimeEdit::emitValueChanged( const QVariant &value )
259 {
260  emit QgsDateTimeEdit::valueChanged( value.toDateTime() );
261 }
262 
264 {
265  return mAllowNull && mIsNull;
266 }
267 
268 void QgsDateTimeEdit::displayCurrentDate()
269 {
270  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
271  QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
272  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
273 }
274 
275 void QgsDateTimeEdit::resetBeforeChange( int delta )
276 {
277  QDateTime dt = QDateTime::currentDateTime();
278  switch ( currentSection() )
279  {
280  case QDateTimeEdit::DaySection:
281  dt = dt.addDays( delta );
282  break;
283  case QDateTimeEdit::MonthSection:
284  dt = dt.addMonths( delta );
285  break;
286  case QDateTimeEdit::YearSection:
287  dt = dt.addYears( delta );
288  break;
289  default:
290  break;
291  }
292  if ( dt < minimumDateTime() )
293  {
294  dt = minimumDateTime();
295  }
296  else if ( dt > maximumDateTime() )
297  {
298  dt = maximumDateTime();
299  }
300  QDateTimeEdit::setDateTime( dt );
301 }
302 
303 void QgsDateTimeEdit::setDateTime( const QDateTime &dateTime )
304 {
305  mIsEmpty = false;
306 
307  // set an undefined date
308  if ( !dateTime.isValid() || dateTime.isNull() )
309  {
310  clear();
311  displayNull();
312  }
313  // Check if it's really changed or crash, see GH #29937
314  else if ( dateTime != QgsDateTimeEdit::dateTime() )
315  {
316  // changed emits a signal, so don't allow it to be emitted from setDateTime
318  QDateTimeEdit::setDateTime( dateTime );
320  changed( dateTime );
321  }
322 }
323 
324 QDateTime QgsDateTimeEdit::dateTime() const
325 {
326  if ( isNull() )
327  {
328  return QDateTime();
329  }
330  else
331  {
332  return QDateTimeEdit::dateTime();
333  }
334 }
335 
337 {
338  if ( isNull() )
339  {
340  return QTime();
341  }
342  else
343  {
344  return QDateTimeEdit::time();
345  }
346 }
347 
349 {
350  if ( isNull() )
351  {
352  return QDate();
353  }
354  else
355  {
356  return QDateTimeEdit::date();
357  }
358 }
359 
360 
361 //
362 // QgsTimeEdit
363 //
364 
365 QgsTimeEdit::QgsTimeEdit( QWidget *parent )
366  : QgsDateTimeEdit( QTime(), QVariant::Time, parent )
367 {
368 
369 }
370 
371 void QgsTimeEdit::setTime( const QTime &time )
372 {
373  mIsEmpty = false;
374 
375  // set an undefined date
376  if ( !time.isValid() || time.isNull() )
377  {
378  clear();
379  displayNull();
380  }
381  // Check if it's really changed or crash, see GH #29937
382  else if ( time != QgsTimeEdit::time() )
383  {
384  // changed emits a signal, so don't allow it to be emitted from setTime
386  QDateTimeEdit::setTime( time );
388  changed( time );
389  }
390 }
391 
392 void QgsTimeEdit::emitValueChanged( const QVariant &value )
393 {
394  emit timeValueChanged( value.toTime() );
395 }
396 
397 
398 //
399 // QgsDateEdit
400 //
401 
402 QgsDateEdit::QgsDateEdit( QWidget *parent )
403  : QgsDateTimeEdit( QDate(), QVariant::Date, parent )
404 {
405 
406 }
407 
408 void QgsDateEdit::setDate( const QDate &date )
409 {
410  mIsEmpty = false;
411 
412  // set an undefined date
413  if ( !date.isValid() || date.isNull() )
414  {
415  clear();
416  displayNull();
417  }
418  // Check if it's really changed or crash, see GH #29937
419  else if ( date != QgsDateEdit::date() )
420  {
421  // changed emits a signal, so don't allow it to be emitted from setDate
423  QDateTimeEdit::setDate( date );
425  changed( date );
426  }
427 }
428 
429 void QgsDateEdit::emitValueChanged( const QVariant &value )
430 {
431  emit dateValueChanged( value.toDate() );
432 }
Extends QApplication to provide access to QGIS specific resources such as theme paths,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void setDate(const QDate &date)
Sets the date for the widget and handles null dates.
void dateValueChanged(const QDate &date)
Signal emitted whenever the date changes.
QgsDateEdit(QWidget *parent=nullptr)
Constructor for QgsDateEdit.
void emitValueChanged(const QVariant &value) override
Emits the widget's correct value changed signal.
The QgsDateTimeEdit class is a QDateTimeEdit with the capability of setting/reading null date/times.
void wheelEvent(QWheelEvent *event) override
void setAllowNull(bool allowNull)
Determines if the widget allows setting null date/time.
void setNullRepresentation(const QString &null)
Sets the widget's null representation, which defaults to QgsApplication::nullRepresentation().
int mBlockChangedSignal
Block change signals if true.
void showEvent(QShowEvent *event) override
QDateTime dateTime() const
Returns the date time which can be a null date/time.
void focusInEvent(QFocusEvent *event) override
bool isNull() const
Returns true if the widget is currently set to a null value.
virtual void emitValueChanged(const QVariant &value)
Emits the widget's correct value changed signal.
void mousePressEvent(QMouseEvent *event) override
void setDateTime(const QDateTime &dateTime)
Set the date time in the widget and handles null date times.
QTime time() const
Returns the time which can be a null time.
void setEmpty()
Resets the widget to show no value (ie, an "unknown" state).
QString nullRepresentation() const
Returns the widget's NULL representation, which defaults to QgsApplication::nullRepresentation().
void focusOutEvent(QFocusEvent *event) override
bool mIsEmpty
true if the widget is empty
void displayNull(bool updateCalendar=false)
write the null value representation to the line edit without changing the value
void clear() override
Set the current date as NULL.
QgsDateTimeEdit(QWidget *parent=nullptr)
Constructor for QgsDateTimeEdit.
QDate date() const
Returns the date which can be a null date.
void valueChanged(const QDateTime &date)
Signal emitted whenever the value changes.
QgsTimeEdit(QWidget *parent=nullptr)
Constructor for QgsTimeEdit.
void emitValueChanged(const QVariant &value) override
Emits the widget's correct value changed signal.
void timeValueChanged(const QTime &time)
Signal emitted whenever the time changes.
void setTime(const QTime &time)
Sets the time for the widget and handles null times.