QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
33  : QgsDateTimeEdit( QDateTime(), QVariant::DateTime, parent )
34 #else
35  : QgsDateTimeEdit( QDateTime(), QMetaType::QDateTime, parent )
36 #endif
37 {
38 
39 }
40 
42 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
43 QgsDateTimeEdit::QgsDateTimeEdit( const QVariant &var, QVariant::Type parserType, QWidget *parent )
44  : QDateTimeEdit( var, parserType, parent )
45 #else
46 QgsDateTimeEdit::QgsDateTimeEdit( const QVariant & var, QMetaType::Type parserType, QWidget * parent )
47  : QDateTimeEdit( var, parserType, parent )
48 #endif
49  , mNullRepresentation( QgsApplication::nullRepresentation() )
50 {
51  const QIcon clearIcon = QgsApplication::getThemeIcon( "/mIconClearText.svg" );
52  mClearAction = new QAction( clearIcon, tr( "clear" ), this );
53  mClearAction->setCheckable( false );
54  lineEdit()->addAction( mClearAction, QLineEdit::TrailingPosition );
55  mClearAction->setVisible( mAllowNull );
56  connect( mClearAction, &QAction::triggered, this, &QgsDateTimeEdit::clear );
57 
58  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
59 
60  // enable calendar widget by default so it's already created
61  setCalendarPopup( true );
62 
63  setMinimumEditDateTime();
64 
65  // init with current time so mIsNull is properly initialized
66  QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
67 }
69 
70 void QgsDateTimeEdit::setAllowNull( bool allowNull )
71 {
72  mAllowNull = allowNull;
73  mClearAction->setVisible( mAllowNull && ( !mIsNull || mIsEmpty ) );
74 }
75 
76 
78 {
79  if ( mAllowNull )
80  {
81  displayCurrentDate();
82 
83  // Check if it's really changed or crash, see GH #29937
84  if ( ! dateTime().isNull() )
85  {
86  changed( QDateTime() );
87  }
88 
89  // emit signal of QDateTime::dateTimeChanged with an invalid date
90  // anyway, using parent's signal should be avoided
91  // If you consequently connect parent's dateTimeChanged signal
92  // and call dateTime() afterwards there is no warranty to
93  // have a proper NULL value handling
94  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
95  emit dateTimeChanged( QDateTime() );
96  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
97  }
98 }
99 
101 {
102  mClearAction->setVisible( mAllowNull );
103  mIsEmpty = true;
104 }
105 
106 void QgsDateTimeEdit::mousePressEvent( QMouseEvent *event )
107 {
108  // catch mouse press on the button (when the current value is null)
109  // in non-calendar mode: modify the date so it leads to showing current date (don't bother about time)
110  // in calendar mode: be sure NULL is displayed when needed and show page of current date in calendar widget
111 
112  bool updateCalendar = false;
113 
114  if ( mIsNull )
115  {
116  QStyle::SubControl control;
117  if ( calendarPopup() )
118  {
119  QStyleOptionComboBox optCombo;
120 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
121  optCombo.init( this );
122 #else
123  optCombo.initFrom( this );
124 #endif
125  optCombo.editable = true;
126  optCombo.subControls = QStyle::SC_All;
127  control = style()->hitTestComplexControl( QStyle::CC_ComboBox, &optCombo, event->pos(), this );
128 
129  if ( control == QStyle::SC_ComboBoxArrow && calendarWidget() )
130  {
131  mCurrentPressEvent = true;
132  // ensure the line edit still displays NULL
133  updateCalendar = true;
134  displayNull( updateCalendar );
135  mCurrentPressEvent = false;
136  }
137  }
138  else
139  {
140  QStyleOptionSpinBox opt;
141  this->initStyleOption( &opt );
142  control = style()->hitTestComplexControl( QStyle::CC_SpinBox, &opt, event->pos(), this );
143 
144  if ( control == QStyle::SC_SpinBoxDown || control == QStyle::SC_SpinBoxUp )
145  {
146  mCurrentPressEvent = true;
147  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
148  resetBeforeChange( control == QStyle::SC_SpinBoxDown ? -1 : 1 );
149  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
150  mCurrentPressEvent = false;
151  }
152  }
153  }
154 
155  QDateTimeEdit::mousePressEvent( event );
156 
157  if ( updateCalendar )
158  {
159  // set calendar page to current date to avoid going to minimal date page when value is null
160  calendarWidget()->setCurrentPage( QDate::currentDate().year(), QDate::currentDate().month() );
161  }
162 }
163 
164 void QgsDateTimeEdit::focusOutEvent( QFocusEvent *event )
165 {
166  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
167  {
168  QAbstractSpinBox::focusOutEvent( event );
169  if ( lineEdit()->text() != mNullRepresentation )
170  {
171  displayNull();
172  }
173  emit editingFinished();
174  }
175  else
176  {
177  QDateTimeEdit::focusOutEvent( event );
178  }
179 }
180 
181 void QgsDateTimeEdit::focusInEvent( QFocusEvent *event )
182 {
183  if ( mAllowNull && mIsNull && !mCurrentPressEvent )
184  {
185  QAbstractSpinBox::focusInEvent( event );
186 
187  displayCurrentDate();
188  }
189  else
190  {
191  QDateTimeEdit::focusInEvent( event );
192  }
193 }
194 
195 void QgsDateTimeEdit::wheelEvent( QWheelEvent *event )
196 {
197  // dateTime might have been set to minimum in calendar mode
198  if ( mAllowNull && mIsNull )
199  {
200  // convert angleDelta to approximate wheel "steps" -- angleDelta is in 1/8 degrees, and according
201  // to Qt docs most mice step in 15 degree increments
202  resetBeforeChange( -event->angleDelta().y() / ( 15 * 8 ) );
203  }
204  QDateTimeEdit::wheelEvent( event );
205 }
206 
207 void QgsDateTimeEdit::showEvent( QShowEvent *event )
208 {
209  QDateTimeEdit::showEvent( event );
210  if ( mAllowNull && mIsNull &&
211  lineEdit()->text() != mNullRepresentation )
212  {
213  displayNull();
214  }
215 }
216 
218 void QgsDateTimeEdit::changed( const QVariant &dateTime )
219 {
220  mIsEmpty = false;
221  const bool isNull = dateTime.isNull();
222  if ( isNull != mIsNull )
223  {
224  mIsNull = isNull;
225  if ( mIsNull )
226  {
227  if ( mOriginalStyleSheet.isNull() )
228  {
229  mOriginalStyleSheet = lineEdit()->styleSheet();
230  }
231  lineEdit()->setStyleSheet( QStringLiteral( "QLineEdit { font-style: italic; color: grey; }" ) );
232  }
233  else
234  {
235  lineEdit()->setStyleSheet( mOriginalStyleSheet );
236  }
237  }
238 
239  mClearAction->setVisible( mAllowNull && !mIsNull );
240  if ( !mBlockChangedSignal )
242 }
244 
246 {
247  return mNullRepresentation;
248 }
249 
250 void QgsDateTimeEdit::setNullRepresentation( const QString &nullRepresentation )
251 {
252  mNullRepresentation = nullRepresentation;
253  if ( mIsNull )
254  {
255  lineEdit()->setText( mNullRepresentation );
256  }
257 }
258 
259 void QgsDateTimeEdit::displayNull( bool updateCalendar )
260 {
261  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
262  if ( updateCalendar )
263  {
264  // set current time to minimum date time to avoid having
265  // a date selected in calendar widget
266  QDateTimeEdit::setDateTime( minimumDateTime() );
267  }
268  lineEdit()->setCursorPosition( lineEdit()->text().length() );
269  lineEdit()->setText( mNullRepresentation );
270  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
271 }
272 
273 void QgsDateTimeEdit::emitValueChanged( const QVariant &value )
274 {
275  emit QgsDateTimeEdit::valueChanged( value.toDateTime() );
276 }
277 
279 {
280  return mAllowNull && mIsNull;
281 }
282 
283 void QgsDateTimeEdit::displayCurrentDate()
284 {
285  disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
286  QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
287  connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
288 }
289 
290 void QgsDateTimeEdit::resetBeforeChange( int delta )
291 {
292  QDateTime dt = QDateTime::currentDateTime();
293  switch ( currentSection() )
294  {
295  case QDateTimeEdit::DaySection:
296  dt = dt.addDays( delta );
297  break;
298  case QDateTimeEdit::MonthSection:
299  dt = dt.addMonths( delta );
300  break;
301  case QDateTimeEdit::YearSection:
302  dt = dt.addYears( delta );
303  break;
304  default:
305  break;
306  }
307  if ( dt < minimumDateTime() )
308  {
309  dt = minimumDateTime();
310  }
311  else if ( dt > maximumDateTime() )
312  {
313  dt = maximumDateTime();
314  }
315  QDateTimeEdit::setDateTime( dt );
316 }
317 
318 void QgsDateTimeEdit::setDateTime( const QDateTime &dateTime )
319 {
320  mIsEmpty = false;
321 
322  // set an undefined date
323  if ( !dateTime.isValid() || dateTime.isNull() )
324  {
325  clear();
326  displayNull();
327  }
328  // Check if it's really changed or crash, see GH #29937
329  else if ( dateTime != QgsDateTimeEdit::dateTime() )
330  {
331  // changed emits a signal, so don't allow it to be emitted from setDateTime
333  QDateTimeEdit::setDateTime( dateTime );
335  changed( dateTime );
336  }
337 }
338 
339 QDateTime QgsDateTimeEdit::dateTime() const
340 {
341  if ( isNull() )
342  {
343  return QDateTime();
344  }
345  else
346  {
347  return QDateTimeEdit::dateTime();
348  }
349 }
350 
352 {
353  if ( isNull() )
354  {
355  return QTime();
356  }
357  else
358  {
359  return QDateTimeEdit::time();
360  }
361 }
362 
364 {
365  if ( isNull() )
366  {
367  return QDate();
368  }
369  else
370  {
371  return QDateTimeEdit::date();
372  }
373 }
374 
375 
376 //
377 // QgsTimeEdit
378 //
379 
380 QgsTimeEdit::QgsTimeEdit( QWidget *parent )
381 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
382  : QgsDateTimeEdit( QTime(), QVariant::Time, parent )
383 #else
384  : QgsDateTimeEdit( QTime(), QMetaType::QTime, parent )
385 #endif
386 {
387 
388 }
389 
390 void QgsTimeEdit::setTime( const QTime &time )
391 {
392  mIsEmpty = false;
393 
394  // set an undefined date
395  if ( !time.isValid() || time.isNull() )
396  {
397  clear();
398  displayNull();
399  }
400  // Check if it's really changed or crash, see GH #29937
401  else if ( time != QgsTimeEdit::time() )
402  {
403  // changed emits a signal, so don't allow it to be emitted from setTime
405  QDateTimeEdit::setTime( time );
407  changed( time );
408  }
409 }
410 
411 void QgsTimeEdit::emitValueChanged( const QVariant &value )
412 {
413  emit timeValueChanged( value.toTime() );
414 }
415 
416 
417 //
418 // QgsDateEdit
419 //
420 
421 QgsDateEdit::QgsDateEdit( QWidget *parent )
422 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
423  : QgsDateTimeEdit( QDate(), QVariant::Date, parent )
424 #else
425  : QgsDateTimeEdit( QDate(), QMetaType::QDate, parent )
426 #endif
427 {
428 
429 }
430 
431 void QgsDateEdit::setDate( const QDate &date )
432 {
433  mIsEmpty = false;
434 
435  // set an undefined date
436  if ( !date.isValid() || date.isNull() )
437  {
438  clear();
439  displayNull();
440  }
441  // Check if it's really changed or crash, see GH #29937
442  else if ( date != QgsDateEdit::date() )
443  {
444  // changed emits a signal, so don't allow it to be emitted from setDate
446  QDateTimeEdit::setDate( date );
448  changed( date );
449  }
450 }
451 
452 void QgsDateEdit::emitValueChanged( const QVariant &value )
453 {
454  emit dateValueChanged( value.toDate() );
455 }
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.