class DateEditor(CustomEditor): """Widget for editing date values""" calendar_action_trigger = QtCore.pyqtSignal() special_date_icon = Icon('tango/16x16/apps/office-calendar.png') def __init__(self, parent = None, editable = True, nullable = True, field_name = 'date', **kwargs): CustomEditor.__init__(self, parent) self.setObjectName( field_name ) self.date_format = local_date_format() self.line_edit = DecoratedLineEdit() self.line_edit.set_minimum_width( len( self.date_format ) ) self.line_edit.set_background_text( 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 = QtGui.QMenu(self) calendar_widget_action = QtGui.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 = QtGui.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(QtGui.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 = QtGui.QHBoxLayout() self.hlayout.addWidget(self.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(self.line_edit) self.line_edit.editingFinished.connect( self.line_edit_finished ) self.line_edit.textEdited.connect(self.text_edited) special_date_menu.triggered.connect(self.set_special_date) def calendar_widget_activated(self, date): self.calendar_action_trigger.emit() self.set_value(date) self.editingFinished.emit() self.line_edit.setFocus() def line_edit_finished(self): self.setProperty( 'value', QtCore.QVariant( 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', QtCore.QVariant( value ) ) if value: qdate = QtCore.QDate(value) formatted_date = qdate.toString(self.date_format) self.line_edit.set_user_input(formatted_date) self.calendar_widget.setSelectedDate(qdate) else: self.line_edit.set_user_input('') self.valueChanged.emit() def text_edited(self, text ): try: date_from_string( self.line_edit.user_input() ) self.line_edit.set_valid(True) self.valueChanged.emit() except ParsingError: self.line_edit.set_valid(False) def get_value(self): try: value = date_from_string( self.line_edit.user_input() ) except ParsingError: value = None return CustomEditor.get_value(self) or value def set_field_attributes(self, editable = True, background_color = None, tooltip = None, **kwargs): self.set_enabled(editable) self.set_background_color(background_color) self.line_edit.setToolTip(unicode(tooltip or '')) def set_background_color(self, background_color): set_background_color_palette( self.line_edit, background_color ) def set_enabled(self, editable=True): self.line_edit.setEnabled(editable) if editable: self.special_date.show() else: self.special_date.hide() def set_special_date(self, action): 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) self.line_edit.setFocus() self.editingFinished.emit()
class VirtualAddressEditor(CustomEditor): def __init__(self, parent = None, editable = True, address_type = None, address_validator = default_address_validator, field_name = 'virtual_address', **kwargs): """ :param address_type: limit the allowed address to be entered to be of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'. If set to None, all types are allowed. Upto now, the corrected address returned by the address validator is not yet taken into account. """ CustomEditor.__init__(self, parent) self.setObjectName( field_name ) self._address_type = address_type self._address_validator = address_validator self.layout = QtGui.QHBoxLayout() self.layout.setContentsMargins( 0, 0, 0, 0) self.combo = QtGui.QComboBox() self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types) self.combo.setEnabled(editable) if address_type: self.combo.setVisible(False) self.layout.addWidget(self.combo) self.editor = DecoratedLineEdit( self ) self.editor.setEnabled(editable) self.editor.set_minimum_width( 30 ) self.layout.addWidget(self.editor) self.setFocusProxy(self.editor) self.editable = editable nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon() self.label = QtGui.QToolButton() self.label.setIcon(nullIcon) self.label.setAutoRaise(True) self.label.setEnabled(False) self.label.setToolButtonStyle(Qt.ToolButtonIconOnly) self.label.setFocusPolicy(Qt.ClickFocus) self.label.clicked.connect( self.mail_click ) self.label.hide() self.layout.addWidget(self.label) self.editor.editingFinished.connect(self.emit_editing_finished) self.editor.textEdited.connect(self.editorValueChanged) self.combo.currentIndexChanged.connect(self.comboIndexChanged) self.setLayout(self.layout) self.checkValue(self.editor.text()) @QtCore.pyqtSlot() def comboIndexChanged(self): self.checkValue(self.editor.text()) self.emit_editing_finished() def set_value(self, value): value = CustomEditor.set_value(self, value) if value: self.editor.setText(value[1]) idx = camelot.types.VirtualAddress.virtual_address_types.index(self._address_type or value[0]) self.combo.setCurrentIndex(idx) icon = Icon('tango/16x16/devices/printer.png').getQIcon() # These icons don't exist any more in the new tango icon set # if str(self.combo.currentText()) == 'phone': # icon = Icon('tango/16x16/devices/phone.png').getQIcon() if str(self.combo.currentText()) == 'fax': icon = Icon('tango/16x16/devices/printer.png').getQIcon() # if str(self.combo.currentText()) == 'mobile': # icon = Icon('tango/16x16/devices/mobile.png').getQIcon() # if str(self.combo.currentText()) == 'im': # icon = Icon('tango/16x16/places/instant-messaging.png').getQIcon() # if str(self.combo.currentText()) == 'pager': # icon = Icon('tango/16x16/devices/pager.png').getQIcon() if str(self.combo.currentText()) == 'email': icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon() #self.label.setFocusPolicy(Qt.StrongFocus) #self.label.setAutoFillBackground(True) self.label.setIcon(icon) self.label.setEnabled( self.editable ) self.label.show() else: self.label.hide() self.label.setIcon(icon) self.label.setEnabled(self.editable) self.label.setToolButtonStyle(Qt.ToolButtonIconOnly) # self.update() # self.label.update() # self.layout.update() self.checkValue(value[1]) def get_value(self): value = (unicode(self.combo.currentText()), unicode(self.editor.text())) return CustomEditor.get_value(self) or value def set_enabled(self, editable=True): self.combo.setEnabled(editable) self.editor.setEnabled(editable) if not editable: self.label.setEnabled(False) else: if self.combo.currentText() == 'email': self.label.setEnabled(True) def checkValue(self, text): address_type = unicode( self.combo.currentText() ) valid, _corrected = self._address_validator( address_type, unicode( text ) ) self.editor.set_valid( valid ) def editorValueChanged(self, text): self.checkValue(text) @QtCore.pyqtSlot() def mail_click(self): address = self.editor.text() url = QtCore.QUrl() url.setUrl( u'mailto:%s?subject=Subject'%unicode(address) ) QtGui.QDesktopServices.openUrl(url) def emit_editing_finished(self): self.value = [] self.value.append(str(self.combo.currentText())) self.value.append(str(self.editor.text())) self.set_value(self.value) # emiting editingFinished without a value for the mechanism itself will lead to # integrity errors if self.value[1]: self.editingFinished.emit() def set_background_color(self, background_color): set_background_color_palette( self.editor, background_color ) def set_field_attributes(self, editable = True, background_color = None, tooltip = None, **kwargs): self.set_enabled(editable) self.set_background_color(background_color) self.setToolTip(unicode(tooltip or ''))
class DateEditor(CustomEditor): """Widget for editing date values""" calendar_action_trigger = QtCore.pyqtSignal() special_date_icon = Icon('tango/16x16/apps/office-calendar.png') def __init__(self, parent=None, editable=True, nullable=True, **kwargs): CustomEditor.__init__(self, parent) self.date_format = local_date_format() self.line_edit = DecoratedLineEdit() self.line_edit.set_background_text( 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 = QtGui.QMenu(self) calendar_widget_action = QtGui.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 = QtGui.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(QtGui.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 = QtGui.QHBoxLayout() self.hlayout.addWidget(self.line_edit) self.hlayout.addWidget(self.special_date) self.hlayout.setContentsMargins(0, 0, 0, 0) self.hlayout.setMargin(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(self.line_edit) self.line_edit.editingFinished.connect( self.line_edit_finished ) self.line_edit.textEdited.connect(self.text_edited) special_date_menu.triggered.connect(self.set_special_date) def calendar_widget_activated(self, date): self.calendar_action_trigger.emit() self.set_value(date) self.editingFinished.emit() def line_edit_finished(self): self.setProperty( 'value', QtCore.QVariant( self.get_value() ) ) self.valueChanged.emit() self.editingFinished.emit() def focusOutEvent(self, event): self.setProperty( 'value', QtCore.QVariant( self.get_value() ) ) self.valueChanged.emit() self.editingFinished.emit() def set_value(self, value): value = CustomEditor.set_value(self, value) self.setProperty( 'value', QtCore.QVariant( value ) ) if value: qdate = QtCore.QDate(value) formatted_date = qdate.toString(self.date_format) self.line_edit.set_user_input(formatted_date) self.calendar_widget.setSelectedDate(qdate) else: self.line_edit.set_user_input('') self.valueChanged.emit() def text_edited(self, text ): try: date_from_string( self.line_edit.user_input() ) self.line_edit.set_valid(True) self.valueChanged.emit() except ParsingError: self.line_edit.set_valid(False) def get_value(self): try: value = date_from_string( self.line_edit.user_input() ) except ParsingError: value = None return CustomEditor.get_value(self) or value def set_enabled(self, editable=True): self.line_edit.setEnabled(editable) if editable: self.special_date.show() else: self.special_date.hide() def set_special_date(self, action): 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) self.editingFinished.emit()
class VirtualAddressEditor(CustomEditor): def __init__(self, parent=None, editable=True, address_type=None, address_validator=default_address_validator, field_name='virtual_address', **kwargs): """ :param address_type: limit the allowed address to be entered to be of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'. If set to None, all types are allowed. Upto now, the corrected address returned by the address validator is not yet taken into account. """ CustomEditor.__init__(self, parent) self.setObjectName(field_name) self._address_type = address_type self._address_validator = address_validator self.layout = QtGui.QHBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.combo = QtGui.QComboBox() self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types) self.combo.setEnabled(editable) if address_type: self.combo.setVisible(False) self.layout.addWidget(self.combo) self.editor = DecoratedLineEdit(self) self.editor.setEnabled(editable) self.editor.set_minimum_width(30) self.layout.addWidget(self.editor) self.setFocusProxy(self.editor) self.editable = editable nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon() self.label = QtGui.QToolButton() self.label.setIcon(nullIcon) self.label.setAutoRaise(True) self.label.setEnabled(False) self.label.setToolButtonStyle(Qt.ToolButtonIconOnly) self.label.setFocusPolicy(Qt.ClickFocus) self.label.clicked.connect(self.mail_click) self.label.hide() self.layout.addWidget(self.label) self.editor.editingFinished.connect(self.emit_editing_finished) self.editor.textEdited.connect(self.editorValueChanged) self.combo.currentIndexChanged.connect(self.comboIndexChanged) self.setLayout(self.layout) self.checkValue(self.editor.text()) @QtCore.pyqtSlot() def comboIndexChanged(self): self.checkValue(self.editor.text()) self.emit_editing_finished() def set_value(self, value): value = CustomEditor.set_value(self, value) if value: self.editor.setText(value[1]) idx = camelot.types.VirtualAddress.virtual_address_types.index( self._address_type or value[0]) self.combo.setCurrentIndex(idx) icon = Icon('tango/16x16/devices/printer.png').getQIcon() # These icons don't exist any more in the new tango icon set # if str(self.combo.currentText()) == 'phone': # icon = Icon('tango/16x16/devices/phone.png').getQIcon() if str(self.combo.currentText()) == 'fax': icon = Icon('tango/16x16/devices/printer.png').getQIcon() # if str(self.combo.currentText()) == 'mobile': # icon = Icon('tango/16x16/devices/mobile.png').getQIcon() # if str(self.combo.currentText()) == 'im': # icon = Icon('tango/16x16/places/instant-messaging.png').getQIcon() # if str(self.combo.currentText()) == 'pager': # icon = Icon('tango/16x16/devices/pager.png').getQIcon() if str(self.combo.currentText()) == 'email': icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon() #self.label.setFocusPolicy(Qt.StrongFocus) #self.label.setAutoFillBackground(True) self.label.setIcon(icon) self.label.setEnabled(self.editable) self.label.show() else: self.label.hide() self.label.setIcon(icon) self.label.setEnabled(self.editable) self.label.setToolButtonStyle(Qt.ToolButtonIconOnly) # self.update() # self.label.update() # self.layout.update() self.checkValue(value[1]) def get_value(self): value = (unicode(self.combo.currentText()), unicode(self.editor.text())) return CustomEditor.get_value(self) or value def set_enabled(self, editable=True): self.combo.setEnabled(editable) self.editor.setEnabled(editable) if not editable: self.label.setEnabled(False) else: if self.combo.currentText() == 'email': self.label.setEnabled(True) def checkValue(self, text): address_type = unicode(self.combo.currentText()) valid, _corrected = self._address_validator(address_type, unicode(text)) self.editor.set_valid(valid) def editorValueChanged(self, text): self.checkValue(text) @QtCore.pyqtSlot() def mail_click(self): address = self.editor.text() url = QtCore.QUrl() url.setUrl(u'mailto:%s?subject=Subject' % unicode(address)) QtGui.QDesktopServices.openUrl(url) def emit_editing_finished(self): self.value = [] self.value.append(str(self.combo.currentText())) self.value.append(str(self.editor.text())) self.set_value(self.value) # emiting editingFinished without a value for the mechanism itself will lead to # integrity errors if self.value[1]: self.editingFinished.emit() def set_background_color(self, background_color): set_background_color_palette(self.editor, background_color) def set_field_attributes(self, editable=True, background_color=None, tooltip=None, **kwargs): self.set_enabled(editable) self.set_background_color(background_color) self.setToolTip(unicode(tooltip or ''))