QGIS API Documentation 3.27.0-Master (f261cc1f8b)
qgsdatetimeedit.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdatetimeedit.cpp
3 --------------------------------------
4 Date : 08.2014
5 Copyright : (C) 2014 Denis Rouzaud
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)
43QgsDateTimeEdit::QgsDateTimeEdit( const QVariant &var, QVariant::Type parserType, QWidget *parent )
44 : QDateTimeEdit( var, parserType, parent )
45#else
46QgsDateTimeEdit::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
70void 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
106void 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
164void 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
181void 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
195void 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
207void QgsDateTimeEdit::showEvent( QShowEvent *event )
208{
209 QDateTimeEdit::showEvent( event );
210 if ( mAllowNull && mIsNull &&
211 lineEdit()->text() != mNullRepresentation )
212 {
213 displayNull();
214 }
215}
216
218void 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
250void QgsDateTimeEdit::setNullRepresentation( const QString &nullRepresentation )
251{
252 mNullRepresentation = nullRepresentation;
253 if ( mIsNull )
254 {
255 lineEdit()->setText( mNullRepresentation );
256 }
257}
258
259void 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
273void QgsDateTimeEdit::emitValueChanged( const QVariant &value )
274{
275 emit QgsDateTimeEdit::valueChanged( value.toDateTime() );
276}
277
279{
280 return mAllowNull && mIsNull;
281}
282
283void 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
290void 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
318void 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
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
380QgsTimeEdit::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
390void 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
411void QgsTimeEdit::emitValueChanged( const QVariant &value )
412{
413 emit timeValueChanged( value.toTime() );
414}
415
416
417//
418// QgsDateEdit
419//
420
421QgsDateEdit::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
431void 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
452void 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.