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