# ============================================================================
#
# Copyright (C) 2007-2016 Conceptive Engineering bvba.
# www.conceptive.be / info@conceptive.be
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Conceptive Engineering nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ============================================================================
import datetime
import six
from ....core.qt import QtGui, QtCore, QtWidgets, Qt, py_to_variant
from .customeditor import CustomEditor, set_background_color_palette
from ...validator import DateValidator
from camelot.view.art import Icon
from camelot.view.utils import local_date_format, date_from_string, ParsingError
from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
from camelot.core.utils import ugettext as _
[docs]class DateEditor(CustomEditor):
"""Widget for editing date values"""
calendar_action_trigger = QtCore.qt_signal()
special_date_icon = Icon('tango/16x16/apps/office-calendar.png')
def __init__(self, parent = None,
editable = True,
nullable = True,
field_name = 'date',
validator = DateValidator(),
**kwargs):
CustomEditor.__init__(self, parent)
self.setSizePolicy( QtGui.QSizePolicy.Preferred,
QtGui.QSizePolicy.Fixed )
self.setObjectName( field_name )
self.date_format = local_date_format()
line_edit = DecoratedLineEdit()
line_edit.setValidator(validator)
line_edit.setObjectName('date_line_edit')
line_edit.set_minimum_width(six.text_type(QtCore.QDate(2000,12,22).toString(self.date_format)))
line_edit.setPlaceholderText(QtCore.QDate(2000,1,1).toString(self.date_format))
# The order of creation of this widgets and their parenting
# seems very sensitive under windows and creates system crashes
# so don't change this without extensive testing on windows
special_date_menu = QtWidgets.QMenu(self)
calendar_widget_action = QtWidgets.QWidgetAction(special_date_menu)
self.calendar_widget = QtGui.QCalendarWidget(special_date_menu)
self.calendar_widget.activated.connect(self.calendar_widget_activated)
self.calendar_widget.clicked.connect(self.calendar_widget_activated)
calendar_widget_action.setDefaultWidget(self.calendar_widget)
self.calendar_action_trigger.connect( special_date_menu.hide )
special_date_menu.addAction(calendar_widget_action)
special_date_menu.addAction(_('Today'))
special_date_menu.addAction(_('Far future'))
self.special_date = QtWidgets.QToolButton(self)
self.special_date.setIcon( self.special_date_icon.getQIcon() )
self.special_date.setAutoRaise(True)
self.special_date.setToolTip(_('Calendar and special dates'))
self.special_date.setMenu(special_date_menu)
self.special_date.setPopupMode(QtWidgets.QToolButton.InstantPopup)
self.special_date.setFixedHeight(self.get_height())
self.special_date.setFocusPolicy(Qt.ClickFocus)
# end of sensitive part
if nullable:
special_date_menu.addAction(_('Clear'))
self.hlayout = QtWidgets.QHBoxLayout()
self.hlayout.addWidget(line_edit)
self.hlayout.addWidget(self.special_date)
self.hlayout.setContentsMargins(0, 0, 0, 0)
self.hlayout.setSpacing(0)
self.hlayout.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
self.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.hlayout)
self.minimum = datetime.date.min
self.maximum = datetime.date.max
self.setFocusProxy(line_edit)
line_edit.editingFinished.connect(self.line_edit_finished)
special_date_menu.triggered.connect(self.set_special_date)
def calendar_widget_activated(self, date):
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
self.calendar_action_trigger.emit()
self.set_value(date)
self.editingFinished.emit()
line_edit.setFocus()
def line_edit_finished(self):
self.setProperty( 'value', py_to_variant( self.get_value() ) )
self.valueChanged.emit()
self.editingFinished.emit()
def focusOutEvent(self, event):
# explicitely set value on focus out to format the date in case
# it was entered unformatted
value = self.get_value()
self.set_value( value )
self.editingFinished.emit()
def set_value(self, value):
value = CustomEditor.set_value(self, value)
self.setProperty( 'value', py_to_variant( value ) )
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
if value:
qdate = QtCore.QDate(value)
formatted_date = qdate.toString(self.date_format)
line_edit.setText(formatted_date)
self.calendar_widget.setSelectedDate(qdate)
else:
line_edit.setText('')
self.valueChanged.emit()
def get_value(self):
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
try:
value = date_from_string( six.text_type( line_edit.text() ) )
except ParsingError:
value = None
return CustomEditor.get_value(self) or value
def set_field_attributes(self, **kwargs):
super(DateEditor, self).set_field_attributes(**kwargs)
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
self.set_enabled(kwargs.get('editable', False))
line_edit.setToolTip(six.text_type(kwargs.get('tooltip') or ''))
def set_background_color(self, background_color):
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
set_background_color_palette(line_edit, background_color)
def set_enabled(self, editable=True):
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
line_edit.setEnabled(editable)
if editable:
self.special_date.show()
else:
self.special_date.hide()
def set_special_date(self, action):
line_edit = self.findChild(QtWidgets.QWidget, 'date_line_edit')
if line_edit is not None:
if action.text().compare(_('Today')) == 0:
self.set_value(datetime.date.today())
elif action.text().compare(_('Far future')) == 0:
self.set_value(datetime.date( year = 2400, month = 12, day = 31 ))
elif action.text().compare(_('Clear')) == 0:
self.set_value(None)
line_edit.setFocus()
self.editingFinished.emit()