class SimpleSearchControl(QtGui.QWidget): """A control that displays a single text field in which search keywords can be typed emits a search and a cancel signal if the user starts or cancels the search """ expand_search_options_signal = QtCore.pyqtSignal() cancel_signal = QtCore.pyqtSignal() search_signal = QtCore.pyqtSignal(str) def __init__(self, parent): QtGui.QWidget.__init__(self, parent) layout = QtGui.QHBoxLayout() layout.setSpacing(0) layout.setMargin(3) # Search button self.search_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/system-search.png').getQIcon() self.search_button.setIcon(icon) self.search_button.setIconSize(QtCore.QSize(14, 14)) self.search_button.setAutoRaise(True) self.search_button.setToolTip(_('Expand or collapse search options')) self.search_button.clicked.connect( self.emit_expand_search_options ) # Search input from camelot.view.controls.decorated_line_edit import DecoratedLineEdit self.search_input = DecoratedLineEdit(self) self.search_input.set_background_text(_('Search...')) self.search_input.setToolTip(_('type words to search for')) #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}') self.search_input.returnPressed.connect( self.emit_search ) self.search_input.textEdited.connect( self.emit_search ) self.setFocusProxy( self.search_input ) # Cancel button self.cancel_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon() self.cancel_button.setIcon(icon) self.cancel_button.setIconSize(QtCore.QSize(14, 14)) self.cancel_button.setAutoRaise(True) self.cancel_button.clicked.connect( self.emit_cancel ) # Setup layout layout.addWidget(self.search_button) layout.addWidget(self.search_input) layout.addWidget(self.cancel_button) self.setLayout(layout) def search(self, search_text): """Start searching for search_text""" self.search_input.setText(search_text) self.emit_search() @QtCore.pyqtSlot() def emit_expand_search_options(self): self.expand_search_options_signal.emit() @QtCore.pyqtSlot() @QtCore.pyqtSlot(str) def emit_search(self, str=''): text = unicode(self.search_input.user_input()) self.search_signal.emit( text ) @QtCore.pyqtSlot() def emit_cancel(self): self.search_input.setText('') self.cancel_signal.emit()
class SimpleSearchControl(QtGui.QWidget): """A control that displays a single text field in which search keywords can be typed emits a search and a cancel signal if the user starts or cancels the search """ expand_search_options_signal = QtCore.pyqtSignal() cancel_signal = QtCore.pyqtSignal() search_signal = QtCore.pyqtSignal(str) def __init__(self, parent): QtGui.QWidget.__init__(self, parent) layout = QtGui.QHBoxLayout() layout.setSpacing(0) layout.setMargin(3) # Search button self.search_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/system-search.png').getQIcon() self.search_button.setIcon(icon) self.search_button.setIconSize(QtCore.QSize(14, 14)) self.search_button.setAutoRaise(True) self.search_button.setToolTip(_('Expand or collapse search options')) self.search_button.clicked.connect(self.emit_expand_search_options) # Search input from camelot.view.controls.decorated_line_edit import DecoratedLineEdit self.search_input = DecoratedLineEdit(self) self.search_input.set_background_text(_('Search...')) self.search_input.setToolTip(_('type words to search for')) #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}') self.search_input.returnPressed.connect(self.emit_search) self.search_input.textEdited.connect(self.emit_search) self.setFocusProxy(self.search_input) # Cancel button self.cancel_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon() self.cancel_button.setIcon(icon) self.cancel_button.setIconSize(QtCore.QSize(14, 14)) self.cancel_button.setAutoRaise(True) self.cancel_button.clicked.connect(self.emit_cancel) # Setup layout layout.addWidget(self.search_button) layout.addWidget(self.search_input) layout.addWidget(self.cancel_button) self.setLayout(layout) def search(self, search_text): """Start searching for search_text""" self.search_input.setText(search_text) self.emit_search() @QtCore.pyqtSlot() def emit_expand_search_options(self): self.expand_search_options_signal.emit() @QtCore.pyqtSlot() @QtCore.pyqtSlot(str) def emit_search(self, str=''): text = unicode(self.search_input.user_input()) self.search_signal.emit(text) @QtCore.pyqtSlot() def emit_cancel(self): self.search_input.setText('') self.cancel_signal.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 FileEditor(CustomEditor): """Widget for editing File fields""" document_pixmap = Icon('tango/16x16/mimetypes/x-office-document.png') def __init__(self, parent=None, storage=None, field_name='file', remove_original=False, actions=[ field_action.DetachFile(), field_action.OpenFile(), field_action.UploadFile(), field_action.SaveFile() ], **kwargs): CustomEditor.__init__(self, parent) self.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.setObjectName(field_name) self.storage = storage self.filename = None # the widget containing the filename self.value = None self.file_name = None self.remove_original = remove_original self.actions = actions self.setup_widget() def setup_widget(self): """Called inside init, overwrite this method for custom file edit widgets""" self.layout = QtWidgets.QHBoxLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) # Filename self.filename = DecoratedLineEdit(self) self.filename.set_minimum_width(20) self.filename.setFocusPolicy(Qt.ClickFocus) # Setup layout self.document_label = QtWidgets.QLabel(self) self.document_label.setPixmap(self.document_pixmap.getQPixmap()) self.layout.addWidget(self.document_label) self.layout.addWidget(self.filename) self.add_actions(self.actions, self.layout) self.setLayout(self.layout) def file_completion_activated(self, index): from camelot.view.storage import create_stored_file source_index = index.model().mapToSource(index) if not self.completions_model.isDir(source_index): path = self.completions_model.filePath(source_index) create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, filename=path, ) def set_value(self, value): value = CustomEditor.set_value(self, value) self.value = value if value is not None: self.filename.setText(value.verbose_name) else: self.filename.setText('') self.update_actions() return value def get_value(self): return CustomEditor.get_value(self) or self.value def set_field_attributes(self, **kwargs): super(FileEditor, self).set_field_attributes(**kwargs) self.set_enabled(kwargs.get('editable', False)) if self.filename: set_background_color_palette(self.filename, kwargs.get('background_color', None)) self.filename.setToolTip(six.text_type( kwargs.get('tooltip') or '')) self.remove_original = kwargs.get('remove_original', False) def set_enabled(self, editable=True): self.filename.setEnabled(editable) self.filename.setReadOnly(not editable) self.document_label.setEnabled(editable) self.setAcceptDrops(editable) # # Drag & Drop # def dragEnterEvent(self, event): event.acceptProposedAction() def dragMoveEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): from camelot.view.storage import create_stored_file if event.mimeData().hasUrls(): url = event.mimeData().urls()[0] filename = url.toLocalFile() if filename: create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, filename=filename, )
class Many2OneEditor(CustomEditor): """Widget for editing many 2 one relations""" arrow_down_key_pressed = QtCore.qt_signal() class CompletionsModel(QtCore.QAbstractListModel): def __init__(self, parent=None): QtCore.QAbstractListModel.__init__(self, parent) self._completions = [] def setCompletions(self, completions): self._completions = completions self.layoutChanged.emit() def data(self, index, role): return py_to_variant(self._completions[index.row()].get(role)) def rowCount(self, index=None): return len(self._completions) def columnCount(self, index=None): return 1 def __init__(self, admin=None, parent=None, editable=True, field_name='manytoone', actions=[ field_action.ClearObject(), field_action.SelectObject(), field_action.NewObject(), field_action.OpenObject() ], **kwargs): """ :param entity_admin : The Admin interface for the object on the one side of the relation """ CustomEditor.__init__(self, parent) self.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.setObjectName(field_name) self.admin = admin self.new_value = None self._entity_representation = '' self.obj = None self._last_highlighted_entity_getter = None self.layout = QtWidgets.QHBoxLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) # Search input self.search_input = DecoratedLineEdit(self) self.search_input.setPlaceholderText(_('Search...')) self.search_input.textEdited.connect(self.textEdited) self.search_input.set_minimum_width(20) self.search_input.arrow_down_key_pressed.connect( self.on_arrow_down_key_pressed) # suppose garbage was entered, we need to refresh the content self.search_input.editingFinished.connect( self.search_input_editing_finished) self.setFocusProxy(self.search_input) # Search Completer self.completer = QtGui.QCompleter() self.completions_model = self.CompletionsModel(self.completer) self.completer.setModel(self.completions_model) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode( QtGui.QCompleter.UnfilteredPopupCompletion) self.completer.activated[QtCore.QModelIndex].connect( self.completionActivated) self.completer.highlighted[QtCore.QModelIndex].connect( self.completion_highlighted) self.search_input.setCompleter(self.completer) # Setup layout self.layout.addWidget(self.search_input) self.setLayout(self.layout) self.add_actions(actions, self.layout) get_signal_handler().connect_signals(self) def set_field_attributes(self, **kwargs): super(Many2OneEditor, self).set_field_attributes(**kwargs) set_background_color_palette(self.search_input, kwargs.get('background_color')) self.search_input.setToolTip(kwargs.get('tooltip') or '') self.search_input.setEnabled(kwargs.get('editable', False)) self.update_actions() def on_arrow_down_key_pressed(self): self.arrow_down_key_pressed.emit() def textEdited(self, text): self._last_highlighted_entity_getter = None text = six.text_type(self.search_input.text()) def create_search_completion(text): return lambda: self.search_completions(text) post(create_search_completion(six.text_type(text)), self.display_search_completions) self.completer.complete() def search_completions(self, text): """Search for object that match text, to fill the list of completions :return: a list of tuples of (dict_of_object_representation, object) """ search_decorator = create_entity_search_query_decorator( self.admin, text) if search_decorator: sresult = [ self.admin.get_search_identifiers(e) for e in search_decorator(self.admin.get_query()).limit(20) ] return text, sresult return text, [] def display_search_completions(self, prefix_and_completions): assert object_thread(self) prefix, completions = prefix_and_completions self.completions_model.setCompletions(completions) self.completer.setCompletionPrefix(prefix) self.completer.complete() def completionActivated(self, index): obj = index.data(Qt.EditRole) self.set_object(variant_to_py(obj)) def completion_highlighted(self, index): obj = index.data(Qt.EditRole) self._last_highlighted_entity_getter = variant_to_py(obj) @QtCore.qt_slot(object, object) def handle_entity_update(self, sender, entity): if entity is self.get_value(): self.set_object(entity, False) @QtCore.qt_slot(object, object) def handle_entity_delete(self, sender, entity): if entity is self.get_value(): self.set_object(None, False) @QtCore.qt_slot(object, object) def handle_entity_create(self, sender, entity): if entity is self.new_value: self.new_value = None self.set_object(entity) def search_input_editing_finished(self): if self.obj is None: # Only try to 'guess' what the user meant when no entity is set # to avoid inappropriate removal of data, (eg when the user presses # Esc, editingfinished will be called as well, and we should not # overwrite the current entity set) if self._last_highlighted_entity_getter: self.set_object(self._last_highlighted_entity_getter) elif self.completions_model.rowCount() == 1: # There is only one possible option index = self.completions_model.index(0, 0) entity_getter = variant_to_py(index.data(Qt.EditRole)) self.set_object(entity_getter) self.search_input.setText(self._entity_representation or u'') def set_value(self, value): """:param value: either ValueLoading, or a function that returns None or the entity to be shown in the editor""" self._last_highlighted_entity_getter = None self.new_value = None value = CustomEditor.set_value(self, value) self.set_object(value, propagate=False) self.update_actions() def get_value(self): """:return: a function that returns the selected entity or ValueLoading or None""" value = CustomEditor.get_value(self) if value is not None: return value return self.obj @QtCore.qt_slot(tuple) def set_instance_representation(self, representation_and_propagate): """Update the gui""" (desc, propagate) = representation_and_propagate self._entity_representation = desc self.search_input.setText(desc or u'') if propagate: self.editingFinished.emit() def set_object(self, obj, propagate=True): self.obj = obj def get_instance_representation(obj, propagate): """Get a representation of the instance""" if obj is not None: return (self.admin.get_verbose_object_name(obj), propagate) return (None, propagate) post( update_wrapper( partial(get_instance_representation, obj, propagate), get_instance_representation), self.set_instance_representation) selected_object = property(fset=set_object)
class LocalFileEditor(CustomEditor): """Widget for browsing local files and directories""" browse_icon = Icon('tango/16x16/places/folder-saved-search.png') def __init__(self, parent=None, field_name='local_file', directory=False, save_as=False, file_filter='All files (*)', **kwargs): CustomEditor.__init__(self, parent) self.setObjectName(field_name) self._directory = directory self._save_as = save_as self._file_filter = file_filter self.setup_widget() def setup_widget(self): """Called inside init, overwrite this method for custom file edit widgets""" layout = QtGui.QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) browse_button = QtGui.QToolButton(self) browse_button.setFocusPolicy(Qt.ClickFocus) browse_button.setIcon(self.browse_icon.getQIcon()) browse_button.setToolTip(_('Browse')) browse_button.setAutoRaise(True) browse_button.clicked.connect(self.browse_button_clicked) self.filename = DecoratedLineEdit(self) self.filename.editingFinished.connect(self.filename_editing_finished) self.setFocusProxy(self.filename) layout.addWidget(self.filename) layout.addWidget(browse_button) self.setLayout(layout) @QtCore.pyqtSlot() def filename_editing_finished(self): self.valueChanged.emit() self.editingFinished.emit() @QtCore.pyqtSlot() def browse_button_clicked(self): current_directory = os.path.dirname(self.get_value()) if self._directory: value = QtGui.QFileDialog.getExistingDirectory( self, directory=current_directory) elif self._save_as: value = QtGui.QFileDialog.getSaveFileName( self, filter=self._file_filter, directory=current_directory) else: value = QtGui.QFileDialog.getOpenFileName( self, filter=self._file_filter, directory=current_directory) value = os.path.abspath(unicode(value)) self.filename.setText(value) self.valueChanged.emit() self.editingFinished.emit() def set_value(self, value): value = CustomEditor.set_value(self, value) if value: self.filename.setText(value) else: self.filename.setText('') self.valueChanged.emit() return value def get_value(self): return CustomEditor.get_value(self) or unicode(self.filename.text()) value = QtCore.pyqtProperty(str, get_value, set_value) def set_field_attributes(self, editable=True, background_color=None, tooltip=None, **kwargs): self.setEnabled(editable) if self.filename: set_background_color_palette(self.filename, background_color) self.filename.setToolTip(unicode(tooltip or ''))
class SimpleSearchControl(AbstractSearchWidget): """A control that displays a single text field in which search keywords can be typed emits a search and a cancel signal if the user starts or cancels the search """ def __init__(self, parent): QtGui.QWidget.__init__(self, parent) layout = QtGui.QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(3, 3, 3, 3) # # The search timer reduced the number of search signals that are # emitted, by waiting for the next keystroke before emitting the # search signal # timer = QtCore.QTimer(self) timer.setInterval(300) timer.setSingleShot(True) timer.setObjectName('timer') timer.timeout.connect(self.emit_search) # Search button self.search_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/system-search.png').getQIcon() self.search_button.setIcon(icon) self.search_button.setIconSize(QtCore.QSize(14, 14)) self.search_button.setAutoRaise(True) self.search_button.setToolTip(_('Expand or collapse search options')) self.search_button.clicked.connect(self.emit_expand_search_options) # Search input from camelot.view.controls.decorated_line_edit import DecoratedLineEdit self.search_input = DecoratedLineEdit(self) self.search_input.set_background_text(_('Search...')) #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}') self.search_input.returnPressed.connect(self.emit_search) self.search_input.textEdited.connect(self._start_search_timer) self.search_input.arrow_down_key_pressed.connect( self.on_arrow_down_key_pressed) self.setFocusProxy(self.search_input) # Cancel button self.cancel_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon() self.cancel_button.setIcon(icon) self.cancel_button.setIconSize(QtCore.QSize(14, 14)) self.cancel_button.setAutoRaise(True) self.cancel_button.clicked.connect(self.emit_cancel) # Setup layout layout.addWidget(self.search_button) layout.addWidget(self.search_input) layout.addWidget(self.cancel_button) self.setLayout(layout) def search(self, search_text): """Start searching for search_text""" self.search_input.setText(search_text) self.emit_search() @QtCore.pyqtSlot() @QtCore.pyqtSlot(str) def _start_search_timer(self, str=''): timer = self.findChild(QtCore.QTimer, 'timer') if timer: timer.stop() timer.start() @QtCore.pyqtSlot() def emit_expand_search_options(self): self.expand_search_options_signal.emit() @QtCore.pyqtSlot() @QtCore.pyqtSlot(str) def emit_search(self, str=''): timer = self.findChild(QtCore.QTimer, 'timer') if timer: timer.stop() text = unicode(self.search_input.user_input()) self.search_signal.emit(text) @QtCore.pyqtSlot() def emit_cancel(self): timer = self.findChild(QtCore.QTimer, 'timer') if timer: timer.stop() self.search_input.setText('') self.cancel_signal.emit() @QtCore.pyqtSlot() def on_arrow_down_key_pressed(self): self.on_arrow_down_signal.emit()
class SimpleSearchControl(AbstractSearchWidget): """A control that displays a single text field in which search keywords can be typed emits a search and a cancel signal if the user starts or cancels the search """ def __init__(self, parent): QtGui.QWidget.__init__(self, parent) layout = QtGui.QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(3, 3, 3, 3) # # The search timer reduced the number of search signals that are # emitted, by waiting for the next keystroke before emitting the # search signal # timer = QtCore.QTimer( self ) timer.setInterval( 300 ) timer.setSingleShot( True ) timer.setObjectName( 'timer' ) timer.timeout.connect( self.emit_search ) # Search button self.search_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/system-search.png').getQIcon() self.search_button.setIcon(icon) self.search_button.setIconSize(QtCore.QSize(14, 14)) self.search_button.setAutoRaise(True) self.search_button.setToolTip(_('Expand or collapse search options')) self.search_button.clicked.connect( self.emit_expand_search_options ) # Search input from camelot.view.controls.decorated_line_edit import DecoratedLineEdit self.search_input = DecoratedLineEdit(self) self.search_input.set_background_text(_('Search...')) #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}') self.search_input.returnPressed.connect( self.emit_search ) self.search_input.textEdited.connect( self._start_search_timer ) self.search_input.arrow_down_key_pressed.connect(self.on_arrow_down_key_pressed) self.setFocusProxy( self.search_input ) # Cancel button self.cancel_button = QtGui.QToolButton() icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon() self.cancel_button.setIcon(icon) self.cancel_button.setIconSize(QtCore.QSize(14, 14)) self.cancel_button.setAutoRaise(True) self.cancel_button.clicked.connect( self.emit_cancel ) # Setup layout layout.addWidget(self.search_button) layout.addWidget(self.search_input) layout.addWidget(self.cancel_button) self.setLayout(layout) def search(self, search_text): """Start searching for search_text""" self.search_input.setText(search_text) self.emit_search() @QtCore.pyqtSlot() @QtCore.pyqtSlot(str) def _start_search_timer(self, str=''): timer = self.findChild( QtCore.QTimer, 'timer' ) if timer: timer.stop() timer.start() @QtCore.pyqtSlot() def emit_expand_search_options(self): self.expand_search_options_signal.emit() @QtCore.pyqtSlot() @QtCore.pyqtSlot(str) def emit_search(self, str=''): timer = self.findChild( QtCore.QTimer, 'timer' ) if timer: timer.stop() text = unicode(self.search_input.user_input()) self.search_signal.emit( text ) @QtCore.pyqtSlot() def emit_cancel(self): timer = self.findChild( QtCore.QTimer, 'timer' ) if timer: timer.stop() self.search_input.setText('') self.cancel_signal.emit() @QtCore.pyqtSlot() def on_arrow_down_key_pressed(self): self.on_arrow_down_signal.emit()
class LocalFileEditor( CustomEditor ): """Widget for browsing local files and directories""" browse_icon = Icon( 'tango/16x16/places/folder-saved-search.png' ) def __init__(self, parent = None, field_name = 'local_file', directory = False, save_as = False, file_filter = 'All files (*)', **kwargs): CustomEditor.__init__(self, parent) self.setObjectName( field_name ) self._directory = directory self._save_as = save_as self._file_filter = file_filter self.setup_widget() def setup_widget(self): """Called inside init, overwrite this method for custom file edit widgets""" layout = QtGui.QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) browse_button = QtGui.QToolButton( self ) browse_button.setFocusPolicy( Qt.ClickFocus ) browse_button.setIcon( self.browse_icon.getQIcon() ) browse_button.setToolTip( _('Browse') ) browse_button.setAutoRaise( True ) browse_button.clicked.connect( self.browse_button_clicked ) self.filename = DecoratedLineEdit(self) self.filename.editingFinished.connect( self.filename_editing_finished ) self.setFocusProxy( self.filename ) layout.addWidget( self.filename ) layout.addWidget( browse_button ) self.setLayout( layout ) @QtCore.pyqtSlot() def filename_editing_finished(self): self.valueChanged.emit() self.editingFinished.emit() @QtCore.pyqtSlot() def browse_button_clicked(self): current_directory = os.path.dirname( self.get_value() ) if self._directory: value = QtGui.QFileDialog.getExistingDirectory( self, directory = current_directory ) elif self._save_as: value = QtGui.QFileDialog.getSaveFileName( self, filter = self._file_filter, directory = current_directory ) else: value = QtGui.QFileDialog.getOpenFileName( self, filter = self._file_filter, directory = current_directory ) value = os.path.abspath( unicode( value ) ) self.filename.setText( value ) self.valueChanged.emit() self.editingFinished.emit() def set_value(self, value): value = CustomEditor.set_value(self, value) if value: self.filename.setText( value ) else: self.filename.setText( '' ) self.valueChanged.emit() return value def get_value(self): return CustomEditor.get_value(self) or unicode( self.filename.text() ) value = QtCore.pyqtProperty( str, get_value, set_value ) def set_field_attributes( self, editable = True, background_color = None, tooltip = None, **kwargs): self.setEnabled( editable ) if self.filename: set_background_color_palette( self.filename, background_color ) self.filename.setToolTip(unicode(tooltip or ''))
class FileEditor(CustomEditor): """Widget for editing File fields""" filter = "All files (*)" add_icon = Icon("tango/16x16/actions/list-add.png") open_icon = Icon("tango/16x16/actions/document-open.png") clear_icon = Icon("tango/16x16/actions/edit-delete.png") save_as_icon = Icon("tango/16x16/actions/document-save-as.png") document_pixmap = Icon("tango/16x16/mimetypes/x-office-document.png") def __init__(self, parent=None, storage=None, field_name="file", remove_original=False, **kwargs): CustomEditor.__init__(self, parent) self.setObjectName(field_name) self.storage = storage self.filename = None # the widget containing the filename self.value = None self.remove_original = remove_original self.setup_widget() def setup_widget(self): """Called inside init, overwrite this method for custom file edit widgets""" self.layout = QtGui.QHBoxLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) # Save As button self.save_as_button = QtGui.QToolButton() self.save_as_button.setFocusPolicy(Qt.ClickFocus) self.save_as_button.setIcon(self.save_as_icon.getQIcon()) self.save_as_button.setToolTip(_("Save file as")) self.save_as_button.setAutoRaise(True) self.save_as_button.clicked.connect(self.save_as_button_clicked) # Clear button self.clear_button = QtGui.QToolButton() self.clear_button.setFocusPolicy(Qt.ClickFocus) self.clear_button.setIcon(self.clear_icon.getQIcon()) self.clear_button.setToolTip(_("Delete file")) self.clear_button.setAutoRaise(True) self.clear_button.clicked.connect(self.clear_button_clicked) # Open button self.open_button = QtGui.QToolButton() self.open_button.setFocusPolicy(Qt.ClickFocus) self.open_button.setIcon(self.open_icon.getQIcon()) self.open_button.setToolTip(_("Open file")) self.open_button.clicked.connect(self.open_button_clicked) self.open_button.setAutoRaise(True) # Add button self.add_button = QtGui.QToolButton() self.add_button.setFocusPolicy(Qt.StrongFocus) self.add_button.setIcon(self.add_icon.getQIcon()) self.add_button.setToolTip(_("Attach file")) self.add_button.clicked.connect(self.add_button_clicked) self.add_button.setAutoRaise(True) # Filename self.filename = DecoratedLineEdit(self) self.filename.set_minimum_width(20) self.filename.setFocusPolicy(Qt.ClickFocus) # Search Completer # # Turn completion off, since it creates a thread per field on a form # # self.completer = QtGui.QCompleter() # self.completions_model = QtGui.QFileSystemModel() # self.completer.setCompletionMode( # QtGui.QCompleter.UnfilteredPopupCompletion # ) # self.completer.setModel( self.completions_model ) # self.completer.activated[QtCore.QModelIndex].connect(self.file_completion_activated) # self.filename.setCompleter( self.completer ) # settings = QtCore.QSettings() # last_path = settings.value('lastpath').toString() # # This setting of a rootPath causes a major delay on Windows, since # # the QFileSystemModel starts to fetch file information in a non- # # blocking way (although the documentation state the opposite). # # On Linux, there is no such delay, so it's safe to set such a root # # path and let the underlaying system start indexing. # import sys # if sys.platform != "win32": # self.completions_model.setRootPath( last_path ) # Setup layout self.document_label = QtGui.QLabel(self) self.document_label.setPixmap(self.document_pixmap.getQPixmap()) self.layout.addWidget(self.document_label) self.layout.addWidget(self.filename) self.layout.addWidget(self.clear_button) self.layout.addWidget(self.open_button) self.layout.addWidget(self.add_button) self.layout.addWidget(self.save_as_button) self.setLayout(self.layout) def file_completion_activated(self, index): from camelot.view.storage import create_stored_file source_index = index.model().mapToSource(index) if not self.completions_model.isDir(source_index): path = self.completions_model.filePath(source_index) create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, filename=path, ) def set_value(self, value): value = CustomEditor.set_value(self, value) self.value = value if value: self.clear_button.setVisible(True) self.save_as_button.setVisible(True) self.open_button.setVisible(True) self.add_button.setVisible(False) self.filename.setText(value.verbose_name) else: self.clear_button.setVisible(False) self.save_as_button.setVisible(False) self.open_button.setVisible(False) self.add_button.setVisible(True) self.filename.setText("") return value def get_value(self): return CustomEditor.get_value(self) or self.value def set_field_attributes(self, editable=True, background_color=None, tooltip=None, remove_original=False, **kwargs): self.set_enabled(editable) if self.filename: set_background_color_palette(self.filename, background_color) self.filename.setToolTip(unicode(tooltip or "")) self.remove_original = remove_original def set_enabled(self, editable=True): self.clear_button.setEnabled(editable) self.add_button.setEnabled(editable) self.filename.setEnabled(editable) self.filename.setReadOnly(not editable) self.document_label.setEnabled(editable) self.setAcceptDrops(editable) def stored_file_ready(self, stored_file): """Slot to be called when a new stored_file has been created by the storage""" self.set_value(stored_file) self.editingFinished.emit() def save_as_button_clicked(self): from camelot.view.storage import save_stored_file value = self.get_value() if value: save_stored_file(self, value) def add_button_clicked(self): from camelot.view.storage import create_stored_file create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original ) def open_button_clicked(self): from camelot.view.storage import open_stored_file open_stored_file(self, self.value) def clear_button_clicked(self): answer = QtGui.QMessageBox.question( self, _("Remove this file ?"), _("If you continue, you will no longer be able to open this file."), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No, ) if answer == QtGui.QMessageBox.Yes: self.value = None self.editingFinished.emit() # # Drag & Drop # def dragEnterEvent(self, event): event.acceptProposedAction() def dragMoveEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): from camelot.view.storage import create_stored_file if event.mimeData().hasUrls(): url = event.mimeData().urls()[0] filename = url.toLocalFile() if filename: create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, filename=filename, )
class FileEditor(CustomEditor): """Widget for editing File fields""" filter = 'All files (*)' add_icon = Icon('tango/16x16/actions/list-add.png') open_icon = Icon('tango/16x16/actions/document-open.png') clear_icon = Icon('tango/16x16/actions/edit-delete.png') save_as_icon = Icon('tango/16x16/actions/document-save-as.png') document_pixmap = Icon('tango/16x16/mimetypes/x-office-document.png') def __init__(self, parent=None, storage=None, field_name='file', remove_original=False, **kwargs): CustomEditor.__init__(self, parent) self.setObjectName(field_name) self.storage = storage self.filename = None # the widget containing the filename self.value = None self.remove_original = remove_original self.setup_widget() def setup_widget(self): """Called inside init, overwrite this method for custom file edit widgets""" self.layout = QtGui.QHBoxLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) # Save As button self.save_as_button = QtGui.QToolButton() self.save_as_button.setFocusPolicy(Qt.ClickFocus) self.save_as_button.setIcon(self.save_as_icon.getQIcon()) self.save_as_button.setToolTip(_('Save file as')) self.save_as_button.setAutoRaise(True) self.save_as_button.clicked.connect(self.save_as_button_clicked) # Clear button self.clear_button = QtGui.QToolButton() self.clear_button.setFocusPolicy(Qt.ClickFocus) self.clear_button.setIcon(self.clear_icon.getQIcon()) self.clear_button.setToolTip(_('Delete file')) self.clear_button.setAutoRaise(True) self.clear_button.clicked.connect(self.clear_button_clicked) # Open button self.open_button = QtGui.QToolButton() self.open_button.setFocusPolicy(Qt.ClickFocus) self.open_button.setIcon(self.open_icon.getQIcon()) self.open_button.setToolTip(_('Open file')) self.open_button.clicked.connect(self.open_button_clicked) self.open_button.setAutoRaise(True) # Add button self.add_button = QtGui.QToolButton() self.add_button.setFocusPolicy(Qt.StrongFocus) self.add_button.setIcon(self.add_icon.getQIcon()) self.add_button.setToolTip(_('Attach file')) self.add_button.clicked.connect(self.add_button_clicked) self.add_button.setAutoRaise(True) # Filename self.filename = DecoratedLineEdit(self) self.filename.set_minimum_width(20) self.filename.setFocusPolicy(Qt.ClickFocus) # Search Completer # # Turn completion off, since it creates a thread per field on a form # # self.completer = QtGui.QCompleter() # self.completions_model = QtGui.QFileSystemModel() # self.completer.setCompletionMode( # QtGui.QCompleter.UnfilteredPopupCompletion # ) # self.completer.setModel( self.completions_model ) # self.completer.activated[QtCore.QModelIndex].connect(self.file_completion_activated) # self.filename.setCompleter( self.completer ) # settings = QtCore.QSettings() # last_path = settings.value('lastpath').toString() # # This setting of a rootPath causes a major delay on Windows, since # # the QFileSystemModel starts to fetch file information in a non- # # blocking way (although the documentation state the opposite). # # On Linux, there is no such delay, so it's safe to set such a root # # path and let the underlaying system start indexing. # import sys # if sys.platform != "win32": # self.completions_model.setRootPath( last_path ) # Setup layout self.document_label = QtGui.QLabel(self) self.document_label.setPixmap(self.document_pixmap.getQPixmap()) self.layout.addWidget(self.document_label) self.layout.addWidget(self.filename) self.layout.addWidget(self.clear_button) self.layout.addWidget(self.open_button) self.layout.addWidget(self.add_button) self.layout.addWidget(self.save_as_button) self.setLayout(self.layout) def file_completion_activated(self, index): from camelot.view.storage import create_stored_file source_index = index.model().mapToSource(index) if not self.completions_model.isDir(source_index): path = self.completions_model.filePath(source_index) create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, filename=path, ) def set_value(self, value): value = CustomEditor.set_value(self, value) self.value = value if value: self.clear_button.setVisible(True) self.save_as_button.setVisible(True) self.open_button.setVisible(True) self.add_button.setVisible(False) self.filename.setText(value.verbose_name) else: self.clear_button.setVisible(False) self.save_as_button.setVisible(False) self.open_button.setVisible(False) self.add_button.setVisible(True) self.filename.setText('') return value def get_value(self): return CustomEditor.get_value(self) or self.value def set_field_attributes(self, editable=True, background_color=None, tooltip=None, remove_original=False, **kwargs): self.set_enabled(editable) if self.filename: set_background_color_palette(self.filename, background_color) self.filename.setToolTip(unicode(tooltip or '')) self.remove_original = remove_original def set_enabled(self, editable=True): self.clear_button.setEnabled(editable) self.add_button.setEnabled(editable) self.filename.setEnabled(editable) self.filename.setReadOnly(not editable) self.document_label.setEnabled(editable) self.setAcceptDrops(editable) def stored_file_ready(self, stored_file): """Slot to be called when a new stored_file has been created by the storage""" self.set_value(stored_file) self.editingFinished.emit() def save_as_button_clicked(self): from camelot.view.storage import save_stored_file value = self.get_value() if value: save_stored_file(self, value) def add_button_clicked(self): from camelot.view.storage import create_stored_file create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, ) def open_button_clicked(self): from camelot.view.storage import open_stored_file open_stored_file(self, self.value) def clear_button_clicked(self): answer = QtGui.QMessageBox.question( self, _('Remove this file ?'), _('If you continue, you will no longer be able to open this file.' ), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if answer == QtGui.QMessageBox.Yes: self.value = None self.editingFinished.emit() # # Drag & Drop # def dragEnterEvent(self, event): event.acceptProposedAction() def dragMoveEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): from camelot.view.storage import create_stored_file if event.mimeData().hasUrls(): url = event.mimeData().urls()[0] filename = url.toLocalFile() if filename: create_stored_file( self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original, filename=filename, )
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 VirtualAddressEditor(CustomEditor): def __init__(self, parent=None, address_type=None, 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.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.setObjectName(field_name) self._address_type = address_type self.layout = QtWidgets.QHBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.combo = QtWidgets.QComboBox() self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types) self.layout.addWidget(self.combo) self.editor = DecoratedLineEdit(self) self.editor.set_minimum_width(30) if address_type: self.combo.setVisible(False) idx = camelot.types.VirtualAddress.virtual_address_types.index( address_type) self.combo.setCurrentIndex(idx) self.layout.addWidget(self.editor) self.setFocusProxy(self.editor) nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon() self.label = QtWidgets.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.combo.currentIndexChanged.connect(self.comboIndexChanged) self.setLayout(self.layout) self.update_validator() @QtCore.qt_slot() def comboIndexChanged(self): self.update_validator() self.emit_editing_finished() def set_value(self, value): value = CustomEditor.set_value(self, value) if value is None: self.editor.setText('') else: 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() if six.text_type(self.combo.currentText()) == 'fax': icon = Icon('tango/16x16/devices/printer.png').getQIcon() if six.text_type(self.combo.currentText()) == 'email': icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon() self.label.setIcon(icon) self.label.show() else: self.label.hide() self.label.setIcon(icon) self.label.setToolButtonStyle(Qt.ToolButtonIconOnly) self.update_validator() def get_value(self): address_value = six.text_type(self.editor.text()) if not len(address_value): value = None else: value = (six.text_type(self.combo.currentText()), address_value) 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 update_validator(self): address_type = six.text_type(self.combo.currentText()) validator = validators.get(address_type, any_character_validator) # change the validator instead of the regexp of the validator to inform # the editor it needs to update its background color self.editor.setValidator(validator) @QtCore.qt_slot() def mail_click(self): address = self.editor.text() url = QtCore.QUrl() url.setUrl(u'mailto:%s?subject=Subject' % six.text_type(address)) QtGui.QDesktopServices.openUrl(url) def emit_editing_finished(self): self.value = [] self.value.append(six.text_type(self.combo.currentText())) self.value.append(six.text_type(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, **kwargs): super(VirtualAddressEditor, self).set_field_attributes(**kwargs) self.set_enabled(kwargs.get('editable', False)) self.setToolTip(six.text_type(kwargs.get('tooltip') or ''))