Ejemplo n.º 1
0
    def _add_groups(self, content, outer):
        """Adds a list of Group objects to the panel, creating a layout if
           needed.  Return the outermost layout.
        """
        # Use the existing layout if there is one.
        if outer is None:
            outer = QtGui.QBoxLayout(self.direction)

        # Process each group.
        for subgroup in content:
            panel = _GroupPanel(subgroup, self.ui).control

            if isinstance(panel, QtGui.QWidget):
                outer.addWidget(panel)
            elif isinstance(panel, QtGui.QLayout):
                if isinstance(panel, QtGui.QBoxLayout):
                    if panel.direction() == QtGui.QBoxLayout.Down:
                        panel.setSpacing(0)
                outer.addLayout(panel)
            else:
                # The sub-group is empty which seems to be used as a way of
                # providing some whitespace.
                outer.addWidget(QtGui.QLabel(' '))

        outer.setSpacing(6)
        return outer
Ejemplo n.º 2
0
    def _add_items(self, content, outer=None):
        """Adds a list of Item objects, creating a layout if needed.  Return
           the outermost layout.
        """
        # Get local references to various objects we need:
        ui = self.ui
        info = ui.info
        handler = ui.handler

        group = self.group
        show_left = group.show_left
        columns = group.columns

        # See if a label is needed.
        show_labels = False
        for item in content:
            show_labels |= item.show_label

        # See if a grid layout is needed.
        if show_labels or columns > 1:
            inner = QtGui.QGridLayout()

            if outer is None:
                outer = inner
            else:
                outer.addLayout(inner)

            row = 0
            if show_left:
                label_alignment = QtCore.Qt.AlignRight
            else:
                label_alignment = QtCore.Qt.AlignLeft

        else:
            # Use the existing layout if there is one.
            if outer is None:
                outer = QtGui.QBoxLayout(self.direction)

            inner = outer

            row = -1
            label_alignment = 0

        # Process each Item in the list:
        col = -1
        for item in content:

            # Keep a track of the current logical row and column unless the
            # layout is not a grid.
            col += 1
            if row >= 0 and col >= columns:
                col = 0
                row += 1

            # Get the name in order to determine its type:
            name = item.name

            # Check if is a label:
            if name == "":
                label = item.label
                if label != "":

                    # Create the label widget.
                    if item.style == "simple":
                        label = QtGui.QLabel(label)
                    else:
                        label = heading_text(None, text=label).control

                    self._add_widget(inner, label, row, col, show_labels)

                    if item.emphasized:
                        self._add_emphasis(label)

                # Continue on to the next Item in the list:
                continue

            # Check if it is a separator:
            if name == "_":
                cols = columns

                # See if the layout is a grid.
                if row >= 0:
                    # Move to the start of the next row if necessary.
                    if col > 0:
                        col = 0
                        row += 1

                    # Skip the row we are about to do.
                    row += 1

                    # Allow for the columns.
                    if show_labels:
                        cols *= 2

                for i in range(cols):
                    line = QtGui.QFrame()

                    if self.direction == QtGui.QBoxLayout.LeftToRight:
                        # Add a vertical separator:
                        line.setFrameShape(QtGui.QFrame.VLine)
                        if row < 0:
                            inner.addWidget(line)
                        else:
                            inner.addWidget(line, i, row)
                    else:
                        # Add a horizontal separator:
                        line.setFrameShape(QtGui.QFrame.HLine)
                        if row < 0:
                            inner.addWidget(line)
                        else:
                            inner.addWidget(line, row, i)

                    line.setFrameShadow(QtGui.QFrame.Sunken)

                # Continue on to the next Item in the list:
                continue

            # Convert a blank to a 5 pixel spacer:
            if name == " ":
                name = "5"

            # Check if it is a spacer:
            if all_digits.match(name):

                # If so, add the appropriate amount of space to the layout:
                n = int(name)
                if self.direction == QtGui.QBoxLayout.LeftToRight:
                    # Add a horizontal spacer:
                    spacer = QtGui.QSpacerItem(n, 1)
                else:
                    # Add a vertical spacer:
                    spacer = QtGui.QSpacerItem(1, n)

                self._add_widget(inner, spacer, row, col, show_labels)

                # Continue on to the next Item in the list:
                continue

            # Otherwise, it must be a trait Item:
            object = eval(item.object_, globals(), ui.context)
            trait = object.base_trait(name)
            desc = trait.tooltip
            if desc is None:
                desc = "Specifies " + trait.desc if trait.desc else ""

            # 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 .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).trait_set(item=item,
                                                    object_name=item.object)

            # 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).
            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

            # Handle any label.
            if item.show_label:
                label = self._create_label(item, ui, desc)
                self._add_widget(inner, label, row, col, show_labels,
                                 label_alignment)
            else:
                label = None

            editor.label_control = label

            # Add emphasis to the editor control if requested:
            if item.emphasized:
                self._add_emphasis(control)

            # If the item wants focus, remember the control so we can set focus
            # immediately before opening the UI.
            if item.has_focus:
                self.ui._focus_control = control

            # Set the correct size on the control, as specified by the user:
            stretch = 0
            item_width = item.width
            item_height = item.height
            if (item_width != -1) or (item_height != -1):
                is_horizontal = self.direction == QtGui.QBoxLayout.LeftToRight

                min_size = control.minimumSizeHint()
                width = min_size.width()
                height = min_size.height()

                force_width = False
                force_height = False

                if (0.0 < item_width <= 1.0) and is_horizontal:
                    stretch = int(100 * item_width)

                item_width = int(item_width)
                if item_width < -1:
                    item_width = -item_width
                    force_width = True
                else:
                    item_width = max(item_width, width)

                if (0.0 < item_height <= 1.0) and (not is_horizontal):
                    stretch = int(100 * item_height)

                item_height = int(item_height)
                if item_height < -1:
                    item_height = -item_height
                    force_height = True
                else:
                    item_height = max(item_height, height)

                control.setMinimumWidth(max(item_width, 0))
                control.setMinimumHeight(max(item_height, 0))
                if (stretch == 0 or not is_horizontal) and force_width:
                    control.setMaximumWidth(item_width)
                if (stretch == 0 or is_horizontal) and force_height:
                    control.setMaximumHeight(item_height)

            # Set size and stretch policies
            self._set_item_size_policy(editor, item, label, stretch)

            # Add the created editor control to the layout
            # FIXME: Need to decide what to do about border_size and padding
            self._add_widget(inner, control, row, col, show_labels)

            # ---- Update the UI object

            # 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 name
            info.bind(id, editor, item.id)

            self.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 handler wants to be notified when the editor is created,
            # add it to the list of methods to be called when the UI is
            # complete:
            defined = getattr(handler, id + "_defined", None)
            if defined is not None:
                ui.add_defined(defined)

            # 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)

        return outer
Ejemplo n.º 3
0
    def __init__(self, group, ui, suppress_label=False):
        """Initialise the object.
        """
        # Get the contents of the group:
        content = group.get_content()

        # Save these for other methods.
        self.group = group
        self.ui = ui

        if group.orientation == "horizontal":
            self.direction = QtGui.QBoxLayout.LeftToRight
        else:
            self.direction = QtGui.QBoxLayout.TopToBottom

        # outer is the top-level widget or layout that will eventually be
        # returned.  sub is the QTabWidget or QToolBox corresponding to any
        # 'tabbed' or 'fold' layout.  It is only used to collapse nested
        # widgets.  inner is the object (not necessarily a layout) that new
        # controls should be added to.
        outer = sub = inner = None

        # Get the group label.
        if suppress_label:
            label = ""
        else:
            label = group.label

        # Create a border if requested.
        if group.show_border:
            outer = QtGui.QGroupBox(label)
            inner = QtGui.QBoxLayout(self.direction, outer)

        elif label != "":
            outer = inner = QtGui.QBoxLayout(self.direction)
            inner.addWidget(heading_text(None, text=label).control)

        # Add the layout specific content.
        if len(content) == 0:
            pass

        elif group.layout == "flow":
            raise NotImplementedError("'the 'flow' layout isn't implemented")

        elif group.layout == "split":
            # Create the splitter.
            splitter = _GroupSplitter(group)
            splitter.setOpaqueResize(False)  # Mimic wx backend resize behavior
            if self.direction == QtGui.QBoxLayout.TopToBottom:
                splitter.setOrientation(QtCore.Qt.Vertical)

            # Make sure the splitter will expand to fill available space
            policy = splitter.sizePolicy()
            policy.setHorizontalStretch(50)
            policy.setVerticalStretch(50)
            if group.orientation == "horizontal":
                policy.setVerticalPolicy(QtGui.QSizePolicy.Expanding)
            else:
                policy.setHorizontalPolicy(QtGui.QSizePolicy.Expanding)
            splitter.setSizePolicy(policy)

            if outer is None:
                outer = splitter
            else:
                inner.addWidget(splitter)

            # Create an editor.
            editor = SplitterGroupEditor(control=outer,
                                         splitter=splitter,
                                         ui=ui)
            self._setup_editor(group, editor)

            self._add_splitter_items(content, splitter)

        elif group.layout in ("tabbed", "fold"):
            # Create the TabWidget or ToolBox.
            if group.layout == "tabbed":
                sub = QtGui.QTabWidget()
            else:
                sub = QtGui.QToolBox()

            # Give tab/tool widget stretch factor equivalent to default stretch
            # factory for a resizeable item. See end of '_add_items'.
            policy = sub.sizePolicy()
            policy.setHorizontalStretch(50)
            policy.setVerticalStretch(50)
            sub.setSizePolicy(policy)

            _fill_panel(sub, content, self.ui, self._add_page_item)

            if outer is None:
                outer = sub
            else:
                inner.addWidget(sub)

            # Create an editor.
            editor = TabbedFoldGroupEditor(container=sub, control=outer, ui=ui)
            self._setup_editor(group, editor)

        else:
            # See if we need to control the visual appearance of the group.
            if group.visible_when != "" or group.enabled_when != "":
                # Make sure that outer is a widget and inner is a layout.
                # Hiding a layout is not properly supported by Qt (the
                # workaround in ``traitsui.qt4.editor._visible_changed_helper``
                # often leaves undesirable blank space).
                if outer is None:
                    outer = inner = QtGui.QBoxLayout(self.direction)
                if isinstance(outer, QtGui.QLayout):
                    widget = QtGui.QWidget()
                    widget.setLayout(outer)
                    outer = widget

                # Create an editor.
                self._setup_editor(group, GroupEditor(control=outer))

            if group.scrollable:
                # ensure a widget rather than a layout for the scroll area
                if outer is None:
                    outer = inner = QtGui.QBoxLayout(self.direction)
                if isinstance(outer, QtGui.QLayout):
                    widget = QtGui.QWidget()
                    widget.setLayout(outer)
                    outer = widget

                # now create a scroll area for the widget
                scroll_area = QtGui.QScrollArea()
                scroll_area.setWidget(outer)
                scroll_area.setWidgetResizable(True)
                outer = scroll_area

            if isinstance(content[0], Group):
                layout = self._add_groups(content, inner)
            else:
                layout = self._add_items(content, inner)
            layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)

            if outer is None:
                outer = layout
            elif layout is not inner:
                inner.addLayout(layout)

        if group.style_sheet:
            if isinstance(outer, QtGui.QLayout):
                inner = outer
                outer = QtGui.QWidget()
                outer.setLayout(inner)

            # ensure this is not empty group
            if isinstance(outer, QtGui.QWidget):
                outer.setStyleSheet(group.style_sheet)

        # Publish the top-level widget, layout or None.
        self.control = outer

        # Publish the optional sub-control.
        self.sub_control = sub
Ejemplo n.º 4
0
    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        if factory.name != '':
            self._object, self._name, self._value = \
                self.parse_extended_name( factory.name )

        # Create a panel to hold the object trait's view:
        if factory.editable:
            self.control = self._panel = parent = QtGui.QWidget()

        # Build the instance selector if needed:
        selectable = factory.selectable
        droppable = factory.droppable
        items = self.items
        for item in items:
            droppable |= item.is_droppable()
            selectable |= item.is_selectable()

        if selectable:
            self._object_cache = {}
            item = self.item_for(self.value)
            if item is not None:
                self._object_cache[id(item)] = self.value

            self._choice = QtGui.QComboBox()
            QtCore.QObject.connect(self._choice,
                                   QtCore.SIGNAL('activated(QString)'),
                                   self.update_object)

            self.set_tooltip(self._choice)

            if factory.name != '':
                self._object.on_trait_change(self.rebuild_items,
                                             self._name,
                                             dispatch='ui')
                self._object.on_trait_change(self.rebuild_items,
                                             self._name + '_items',
                                             dispatch='ui')

            factory.on_trait_change(self.rebuild_items,
                                    'values',
                                    dispatch='ui')
            factory.on_trait_change(self.rebuild_items,
                                    'values_items',
                                    dispatch='ui')

            self.rebuild_items()

        elif droppable:
            self._choice = QtGui.QLineEdit()
            self._choice.setReadOnly(True)
            self.set_tooltip(self._choice)

        if droppable:
            self._choice.SetDropTarget(PythonDropTarget(self))

        orientation = OrientationMap[factory.orientation]
        if orientation is None:
            orientation = self.orientation

        if (selectable or droppable) and factory.editable:
            layout = QtGui.QBoxLayout(orientation, parent)
            layout.setContentsMargins(0, 0, 0, 0)
            layout.addWidget(self._choice)

            if orientation == QtGui.QBoxLayout.TopToBottom:
                hline = QtGui.QFrame()
                hline.setFrameShape(QtGui.QFrame.HLine)
                hline.setFrameShadow(QtGui.QFrame.Sunken)

                layout.addWidget(hline)

            self.create_editor(parent, layout)
        elif self.control is None:
            if self._choice is None:
                self._choice = QtGui.QComboBox()
                QtCore.QObject.connect(self._choice,
                                       QtCore.SIGNAL('activated(QString)'),
                                       self.update_object)

            self.control = self._choice
        else:
            layout = QtGui.QBoxLayout(orientation, parent)
            layout.setContentsMargins(0, 0, 0, 0)
            self.create_editor(parent, layout)

        # Synchronize the 'view' to use:
        # fixme: A normal assignment can cause a crash (for unknown reasons) in
        # some cases, so we make sure that no notifications are generated:
        self.trait_setq(view=factory.view)
        self.sync_value(factory.view_name, 'view', 'from')
Ejemplo n.º 5
0
    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        if factory.name != "":
            self._object, self._name, self._value = self.parse_extended_name(
                factory.name
            )

        # Create a panel to hold the object trait's view:
        if factory.editable:
            self.control = self._panel = parent = QtGui.QWidget()

        # Build the instance selector if needed:
        selectable = factory.selectable
        droppable = factory.droppable
        items = self.items
        for item in items:
            droppable |= item.is_droppable()
            selectable |= item.is_selectable()

        if selectable:
            self._object_cache = {}
            item = self.item_for(self.value)
            if item is not None:
                self._object_cache[id(item)] = self.value

            self._choice = QtGui.QComboBox()
            self._choice.activated.connect(self.update_object)

            self.set_tooltip(self._choice)

            if factory.name != "":
                self._object.observe(
                    self.rebuild_items, self._name + ".items", dispatch="ui"
                )

            factory.observe(
                self.rebuild_items, "values.items", dispatch="ui"
            )

            self.rebuild_items()

        elif droppable:
            self._choice = QtGui.QLineEdit()
            self._choice.setReadOnly(True)
            self.set_tooltip(self._choice)

        if droppable:
            # Install EventFilter on control to handle DND events.
            drop_event_filter = _DropEventFilter(self.control)
            self.control.installEventFilter(drop_event_filter)

        orientation = OrientationMap[factory.orientation]
        if orientation is None:
            orientation = self.orientation

        if (selectable or droppable) and factory.editable:
            layout = QtGui.QBoxLayout(orientation, parent)
            layout.setContentsMargins(0, 0, 0, 0)
            layout.addWidget(self._choice)

            if orientation == QtGui.QBoxLayout.TopToBottom:
                hline = QtGui.QFrame()
                hline.setFrameShape(QtGui.QFrame.HLine)
                hline.setFrameShadow(QtGui.QFrame.Sunken)

                layout.addWidget(hline)

            self.create_editor(parent, layout)
        elif self.control is None:
            if self._choice is None:
                self._choice = QtGui.QComboBox()
                self._choice.activated[int].connect(self.update_object)

            self.control = self._choice
        else:
            layout = QtGui.QBoxLayout(orientation, parent)
            layout.setContentsMargins(0, 0, 0, 0)
            self.create_editor(parent, layout)

        # Synchronize the 'view' to use:
        # fixme: A normal assignment can cause a crash (for unknown reasons) in
        # some cases, so we make sure that no notifications are generated:
        self.trait_setq(view=factory.view)
        self.sync_value(factory.view_name, "view", "from")