QGIS API Documentation 3.41.0-Master (3440c17df1d)
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}
40
42#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
43QgsDateTimeEdit::QgsDateTimeEdit( const QVariant &var, QMetaType::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( !isReadOnly() && 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( QVariant() );
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
106bool QgsDateTimeEdit::event( QEvent *event )
107{
108 if ( event->type() == QEvent::ReadOnlyChange || event->type() == QEvent::EnabledChange )
109 {
110 mClearAction->setVisible( !isReadOnly() && mAllowNull && ( !mIsNull || mIsEmpty ) );
111 }
112
113 return QDateTimeEdit::event( event );
114}
115
116void QgsDateTimeEdit::mousePressEvent( QMouseEvent *event )
117{
118 // catch mouse press on the button (when the current value is null)
119 // in non-calendar mode: modify the date so it leads to showing current date (don't bother about time)
120 // in calendar mode: be sure NULL is displayed when needed and show page of current date in calendar widget
121
122 bool updateCalendar = false;
123
124 if ( mIsNull )
125 {
126 QStyle::SubControl control;
127 if ( calendarPopup() )
128 {
129 QStyleOptionComboBox optCombo;
130#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
131 optCombo.init( this );
132#else
133 optCombo.initFrom( this );
134#endif
135 optCombo.editable = true;
136 optCombo.subControls = QStyle::SC_All;
137 control = style()->hitTestComplexControl( QStyle::CC_ComboBox, &optCombo, event->pos(), this );
138
139 if ( control == QStyle::SC_ComboBoxArrow && calendarWidget() )
140 {
141 mCurrentPressEvent = true;
142 // ensure the line edit still displays NULL
143 updateCalendar = true;
144 displayNull( updateCalendar );
145 mCurrentPressEvent = false;
146 }
147 }
148 else
149 {
150 QStyleOptionSpinBox opt;
151 this->initStyleOption( &opt );
152 control = style()->hitTestComplexControl( QStyle::CC_SpinBox, &opt, event->pos(), this );
153
154 if ( control == QStyle::SC_SpinBoxDown || control == QStyle::SC_SpinBoxUp )
155 {
156 mCurrentPressEvent = true;
157 disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
158 resetBeforeChange( control == QStyle::SC_SpinBoxDown ? -1 : 1 );
159 connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
160 mCurrentPressEvent = false;
161 }
162 }
163 }
164
165 QDateTimeEdit::mousePressEvent( event );
166
167 if ( updateCalendar )
168 {
169 // set calendar page to current date to avoid going to minimal date page when value is null
170 calendarWidget()->setCurrentPage( QDate::currentDate().year(), QDate::currentDate().month() );
171 }
172}
173
174void QgsDateTimeEdit::focusOutEvent( QFocusEvent *event )
175{
176 if ( mAllowNull && mIsNull && !mCurrentPressEvent )
177 {
178 QAbstractSpinBox::focusOutEvent( event );
179 if ( lineEdit()->text() != mNullRepresentation )
180 {
181 displayNull();
182 }
183 emit editingFinished();
184 }
185 else
186 {
187 QDateTimeEdit::focusOutEvent( event );
188 }
189}
190
191void QgsDateTimeEdit::focusInEvent( QFocusEvent *event )
192{
193 if ( mAllowNull && mIsNull && !mCurrentPressEvent )
194 {
195 QAbstractSpinBox::focusInEvent( event );
196
197 displayCurrentDate();
198 }
199 else
200 {
201 QDateTimeEdit::focusInEvent( event );
202 }
203}
204
205void QgsDateTimeEdit::wheelEvent( QWheelEvent *event )
206{
207 // dateTime might have been set to minimum in calendar mode
208 if ( mAllowNull && mIsNull )
209 {
210 // convert angleDelta to approximate wheel "steps" -- angleDelta is in 1/8 degrees, and according
211 // to Qt docs most mice step in 15 degree increments
212 resetBeforeChange( -event->angleDelta().y() / ( 15 * 8 ) );
213 }
214 QDateTimeEdit::wheelEvent( event );
215}
216
217void QgsDateTimeEdit::showEvent( QShowEvent *event )
218{
219 QDateTimeEdit::showEvent( event );
220 if ( mAllowNull && mIsNull &&
221 lineEdit()->text() != mNullRepresentation )
222 {
223 displayNull();
224 }
225}
226
228void QgsDateTimeEdit::changed( const QVariant &dateTime )
229{
230 mIsEmpty = false;
232 if ( isNull != mIsNull )
233 {
234 mIsNull = isNull;
235 if ( mIsNull )
236 {
237 if ( mOriginalStyleSheet.isNull() )
238 {
239 mOriginalStyleSheet = lineEdit()->styleSheet();
240 }
241 lineEdit()->setStyleSheet( QStringLiteral( "QLineEdit { font-style: italic; color: grey; }" ) );
242 }
243 else
244 {
245 lineEdit()->setStyleSheet( mOriginalStyleSheet );
246 }
247 }
248
249 mClearAction->setVisible( mAllowNull && !mIsNull );
250 if ( !mBlockChangedSignal )
251 emitValueChanged( isNull ? QVariant() : dateTime );
252}
254
256{
257 return mNullRepresentation;
258}
259
260void QgsDateTimeEdit::setNullRepresentation( const QString &nullRepresentation )
261{
262 mNullRepresentation = nullRepresentation;
263 if ( mIsNull )
264 {
265 lineEdit()->setText( mNullRepresentation );
266 }
267}
268
269void QgsDateTimeEdit::displayNull( bool updateCalendar )
270{
271 disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
272 if ( updateCalendar )
273 {
274 // set current time to minimum date time to avoid having
275 // a date selected in calendar widget
276 QDateTimeEdit::setDateTime( minimumDateTime() );
277 }
278 lineEdit()->setCursorPosition( lineEdit()->text().length() );
279 lineEdit()->setText( mNullRepresentation );
280 connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
281}
282
283void QgsDateTimeEdit::emitValueChanged( const QVariant &value )
284{
285 emit QgsDateTimeEdit::valueChanged( value.toDateTime() );
286}
287
289{
290 return mAllowNull && mIsNull;
291}
292
293void QgsDateTimeEdit::displayCurrentDate()
294{
295 disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
296 QDateTimeEdit::setDateTime( QDateTime::currentDateTime() );
297 connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed );
298}
299
300void QgsDateTimeEdit::resetBeforeChange( int delta )
301{
302 QDateTime dt = QDateTime::currentDateTime();
303 switch ( currentSection() )
304 {
305 case QDateTimeEdit::DaySection:
306 dt = dt.addDays( delta );
307 break;
308 case QDateTimeEdit::MonthSection:
309 dt = dt.addMonths( delta );
310 break;
311 case QDateTimeEdit::YearSection:
312 dt = dt.addYears( delta );
313 break;
314 default:
315 break;
316 }
317 if ( dt < minimumDateTime() )
318 {
319 dt = minimumDateTime();
320 }
321 else if ( dt > maximumDateTime() )
322 {
323 dt = maximumDateTime();
324 }
325 QDateTimeEdit::setDateTime( dt );
326}
327
328void QgsDateTimeEdit::setMinimumEditDateTime()
329{
330 setDateRange( QDate( 1, 1, 1 ), maximumDate() );
331 setMinimumTime( QTime( 0, 0, 0 ) );
332 setMaximumTime( QTime( 23, 59, 59, 999 ) );
333}
334
335void QgsDateTimeEdit::setDateTime( const QDateTime &dateTime )
336{
337 mIsEmpty = false;
338
339 // set an undefined date
340 if ( !dateTime.isValid() || dateTime.isNull() )
341 {
342 clear();
343 displayNull();
344 }
345 // Check if it's really changed or crash, see GH #29937
346 else if ( dateTime != QgsDateTimeEdit::dateTime() )
347 {
348 // changed emits a signal, so don't allow it to be emitted from setDateTime
350 // We need to set the time spec of the set datetime to the widget, otherwise
351 // the dateTime() getter would loose edit, and return local time.
352 QDateTimeEdit::setTimeSpec( dateTime.timeSpec() );
353 QDateTimeEdit::setDateTime( dateTime );
355 changed( dateTime );
356 }
357}
358
360{
361 if ( isNull() )
362 {
363 return QDateTime();
364 }
365 else
366 {
367 return QDateTimeEdit::dateTime();
368 }
369}
370
372{
373 if ( isNull() )
374 {
375 return QTime();
376 }
377 else
378 {
379 return QDateTimeEdit::time();
380 }
381}
382
384{
385 if ( isNull() )
386 {
387 return QDate();
388 }
389 else
390 {
391 return QDateTimeEdit::date();
392 }
393}
394
395
396//
397// QgsTimeEdit
398//
399
400QgsTimeEdit::QgsTimeEdit( QWidget *parent )
401#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
402 : QgsDateTimeEdit( QTime(), QMetaType::Type::QTime, parent )
403#else
404 : QgsDateTimeEdit( QTime(), QMetaType::QTime, parent )
405#endif
406{
407
408}
409
410void QgsTimeEdit::setTime( const QTime &time )
411{
412 mIsEmpty = false;
413
414 // set an undefined date
415 if ( !time.isValid() || time.isNull() )
416 {
417 clear();
418 displayNull();
419 }
420 // Check if it's really changed or crash, see GH #29937
421 else if ( time != QgsTimeEdit::time() )
422 {
423 // changed emits a signal, so don't allow it to be emitted from setTime
425 QDateTimeEdit::setTime( time );
427 changed( time );
428 }
429}
430
431void QgsTimeEdit::emitValueChanged( const QVariant &value )
432{
433 emit timeValueChanged( value.toTime() );
434}
435
436
437//
438// QgsDateEdit
439//
440
441QgsDateEdit::QgsDateEdit( QWidget *parent )
442#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
443 : QgsDateTimeEdit( QDate(), QMetaType::Type::QDate, parent )
444#else
445 : QgsDateTimeEdit( QDate(), QMetaType::QDate, parent )
446#endif
447{
448
449}
450
451void QgsDateEdit::setDate( const QDate &date )
452{
453 mIsEmpty = false;
454
455 // set an undefined date
456 if ( !date.isValid() || date.isNull() )
457 {
458 clear();
459 displayNull();
460 }
461 // Check if it's really changed or crash, see GH #29937
462 else if ( date != QgsDateEdit::date() )
463 {
464 // changed emits a signal, so don't allow it to be emitted from setDate
466 QDateTimeEdit::setDate( date );
468 changed( date );
469 }
470}
471
472void QgsDateEdit::emitValueChanged( const QVariant &value )
473{
474 emit dateValueChanged( value.toDate() );
475}
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.