Source code for qt_binder.bound_editor

#------------------------------------------------------------------------------
#
#  Copyright (c) 2014-2015, Enthought, Inc.
#  All rights reserved.
#
#  This software is provided without warranty under the terms of the BSD
#  license included in LICENSE.txt and may be redistributed only
#  under the conditions described in the aforementioned license.  The license
#  is also available online at http://www.enthought.com/licenses/BSD.txt
#
#  Thanks for using Enthought open source!
#
#------------------------------------------------------------------------------

from traits.api import Any, Callable, Dict, Either, HasPrivateTraits, \
    Instance, List, Str, Undefined
from traitsui.editor_factory import EditorFactory
from traitsui.item import Item
from traitsui.qt4.editor import Editor

from .binder import Binder
from .binding import Binding
from .qt import QtGui
from .raw_widgets import ButtonGroup


[docs]class TraitsUI(Binder): """ Place a Traits UI `Item` into a `Bound` layout. The automatically-added traits are only those for `QWidget`, not whatever widget the root control of the `Item` may turn out to be. This :class:`~.Binder` can only be used in the context of a :class:`~.Bound` layout because it needs to be specially recognized and initialized. """ qclass = QtGui.QWidget #: The Traits UI Item to display. Any label is ignored. item = Instance(Item) # The `UI` object of the whole `View`. _ui = Any() # The initialized editor. _editor = Any() def __init__(self, item=None, **traits): super(TraitsUI, self).__init__(item=item, **traits)
[docs] def initialize_item(self, ui): """ Initialize the item using the Traits UI `UI` object. """ item = self.item name = item.name object = eval(item.object_, globals(), ui.context) trait = object.base_trait(name) # Get the editor factory associated with the Item: editor_factory = item.editor if editor_factory is None: editor_factory = trait.get_editor().trait_set(**item.editor_args) # If still no editor factory found, use a default text editor: if editor_factory is None: from traitsui.qt4.text_editor import ToolkitEditorFactory editor_factory = ToolkitEditorFactory() # If the item has formatting traits set them in the editor # factory: if item.format_func is not None: editor_factory.format_func = item.format_func if item.format_str != '': editor_factory.format_str = item.format_str # If the item has an invalid state extended trait name, set it # in the editor factory: if item.invalid != '': editor_factory.invalid = item.invalid # Create the requested type of editor from the editor factory: factory_method = getattr(editor_factory, item.style + '_editor') editor = factory_method(ui, object, name, item.tooltip, None) editor.trait_set( item=item, object_name=item.object, ) self._editor = editor self._ui = ui
def construct(self): item = self.item editor = self._editor ui = self._ui # Tell the editor to actually build the editing widget. Note that # "inner" is a layout. This shouldn't matter as individual editors # shouldn't be using it as a parent anyway. The important thing is # that it is not None (otherwise the main TraitsUI code can change # the "kind" of the created UI object). inner = QtGui.QVBoxLayout() editor.prepare(inner) control = editor.control if item.style_sheet: control.setStyleSheet(item.style_sheet) # Set the initial 'enabled' state of the editor from the factory: editor.enabled = editor.factory.enabled # Give the editor focus if it requested it: if item.has_focus: control.setFocus() # We ignore the geometry options of the Item. At least for now. # Bind the editor into the UIInfo object name space so it can be # referred to by a Handler while the user interface is active: id = item.id or item.name ui.info.bind(id, editor, item.id) ui._scrollable |= editor.scrollable # Also, add the editors to the list of editors used to construct # the user interface: ui._editors.append(editor) # If the editor is conditionally visible, add the visibility # 'expression' and the editor to the UI object's list of monitored # objects: if item.visible_when != '': ui.add_visible(item.visible_when, editor) # If the editor is conditionally enabled, add the enabling # 'expression' and the editor to the UI object's list of monitored # objects: if item.enabled_when != '': ui.add_enabled(item.enabled_when, editor) self.qobj = control
class QtBoundEditor(Editor): """ Qt implementation of the ``BoundEditor``. """ #: The ``Binder`` object being displayed. binder = Instance(Binder) def __init__(self, parent, **traits): """ Initializes the editor object. """ HasPrivateTraits.__init__(self, **traits) self.old_value = Undefined def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ binder = self.factory.binder for child in binder: if isinstance(child, TraitsUI): child.initialize_item(self.ui) binder.construct() binder.configure() self.binder = binder context = self._get_context() for button_group in self.factory.button_groups.values(): button_group.construct() button_group.configure() button_group.add_buttons_from_context(context) for binding in self.factory.bindings: binding.bind(binder, context) if self.factory.configure is not None: self.factory.configure(binder, context) if isinstance(binder.qobj, QtGui.QLayout): self.control = QtGui.QWidget() self.control.setLayout(binder.qobj) else: self.control = binder.qobj if self.factory.stylesheet is not None: self.control.setStyleSheet(self.factory.stylesheet) def dispose(self): """ Disposes of the contents of an editor. """ if self.ui is None: return for binding in self.factory.bindings: binding.unbind() self.binder.dispose() self.binder = None # Break linkages to references we no longer need: self.object = self.ui = self.item = self.factory = self.control = \ self.label_control = self.old_value = self._context_object = None def update_editor(self): """ Updates the editor when the object trait changes externally to the editor. No-op in our case. """ pass def _get_context(self): """ Return a context for evaluating binding expressions. """ context = self.ui.context.copy() context.update(self.factory.button_groups) context.update(self.factory.extra_context) # Add any binders with IDs to the context. for child in self.binder: if child.id: context[child.id] = child return context class BoundEditor(EditorFactory): """ Editor for Binder-wrapped Qt widgets. """ #: The Binder instance. binder = Instance(Binder) #: The list of ``Bindings``. bindings = List(Instance(Binding)) #: Any extra data to put into the context. extra_context = Dict() #: A function to call to configure the Binder. It takes two arguments, the #: root Binder and the dictionary that is used as the context for #: evaluating the bindings. configure = Callable() #: The Qt stylesheet to apply to the root control. stylesheet = Either(Str, None) #: Mapping of names to `ButtonGroups`. button_groups = Dict(Str, Instance(ButtonGroup)) def _get_simple_editor_class(self): return QtBoundEditor def _get_custom_editor_class(self): return QtBoundEditor def _get_text_editor_class(self): return QtBoundEditor def _get_readonly_editor_class(self): return QtBoundEditor
[docs]class Bound(Item): """ Convenience ``Item`` for placing a ``Binder`` in a ``View``. """ def __init__(self, binder, *bindings, **kwds): extra_context = kwds.pop('extra_context', {}) if 'label' not in kwds: kwds.setdefault('show_label', False) configure = kwds.pop('configure', None) stylesheet = kwds.pop('stylesheet', None) button_groups = kwds.pop('button_groups', {}) # FIXME: find a better workaround for using `trait_modified`. # We use it because Traits UI expects *a* trait here. This is one that # is unlikely to appear elsewhere in Traits UIs. Fortunately, it is one # that every `HasTraits` class already has, and it is an `Event`, which # prevents Traits UI from trying to get its value. parsed_bindings = map(Binding.parse, bindings) super(Bound, self).__init__( value='trait_modified', editor=BoundEditor( binder=binder, bindings=parsed_bindings, extra_context=extra_context, configure=configure, stylesheet=stylesheet, button_groups=button_groups, ), **kwds) def __repr__(self): lines = [ '{0.__name__}('.format(type(self)), ' {0!r},'.format(self.editor.binder), ] for binding in self.editor.bindings: if type(binding).__str__ is not object.__str__: lines.append(" '{0}',".format(binding)) else: lines.append(' {0!r},'.format(binding)) if self.editor.configure: lines.append(' configure={0.configure!r},'.format(self.editor)) if self.editor.extra_context: lines.append(' extra_context={0.extra_context!r},'.format( self.editor)) if self.editor.button_groups: lines.append(' button_groups={0.button_groups!r},'.format( self.editor)) if self.show_label: lines.append(' label={0.label!r},'.format(self)) lines.append(')') return '\n'.join(lines)