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