QGIS API Documentation  3.17.0-Master (a84647cf30)
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 
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 )
226  emitValueChanged( dateTime );
227 }
229 
231 {
232  return mNullRepresentation;
233 }
234 
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 }
QTime time() const
Returns the time which can be a null time.
void emitValueChanged(const QVariant &value) override
Emits the widget&#39;s correct value changed signal.
bool allowNull() const
If the widget allows setting null date/time.
void dateValueChanged(const QDate &date)
Signal emitted whenever the date changes.
void showEvent(QShowEvent *event) override
void setTime(const QTime &time)
Sets the time for the widget and handles null times.
bool mIsEmpty
true if the widget is empty
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void focusOutEvent(QFocusEvent *event) override
void wheelEvent(QWheelEvent *event) override
void focusInEvent(QFocusEvent *event) override
int mBlockChangedSignal
Block change signals if true.
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
QgsTimeEdit(QWidget *parent=nullptr)
Constructor for QgsTimeEdit.
void emitValueChanged(const QVariant &value) override
Emits the widget&#39;s correct value changed signal.
void timeValueChanged(const QTime &time)
Signal emitted whenever the time changes.
virtual void emitValueChanged(const QVariant &value)
Emits the widget&#39;s correct value changed signal.
void valueChanged(const QDateTime &date)
Signal emitted whenever the value changes.
void displayNull(bool updateCalendar=false)
write the null value representation to the line edit without changing the value
void setEmpty()
Resets the widget to show no value (ie, an "unknown" state).
void setAllowNull(bool allowNull)
Determines if the widget allows setting null date/time.
QgsDateTimeEdit(QWidget *parent=nullptr)
Constructor for QgsDateTimeEdit.
void setDateTime(const QDateTime &dateTime)
Set the date time in the widget and handles null date times.
QDate date() const
Returns the date which can be a null date.
void clear() override
Set the current date as NULL.
QDateTime dateTime() const
Returns the date time which can be a null date/time.
void setDate(const QDate &date)
Sets the date for the widget and handles null dates.
void setNullRepresentation(const QString &null)
Sets the widget&#39;s null representation, which defaults to QgsApplication::nullRepresentation().
The QgsDateTimeEdit class is a QDateTimeEdit with the capability of setting/reading null date/times...
QgsDateEdit(QWidget *parent=nullptr)
Constructor for QgsDateEdit.
bool isNull() const
Returns true if the widget is currently set to a null value.
void mousePressEvent(QMouseEvent *event) override
QString nullRepresentation() const
Returns the widget&#39;s NULL representation, which defaults to QgsApplication::nullRepresentation().