Ejemplo n.º 1
0
def add_operator_storage(klass):
    """ Add a member to the class named '__op_storage__'.

    This member will be added as needed, and can be used to store
    instance specific data needed by the operators. The value of the
    storage will be a sortedmap.

    """
    members = klass.members()
    if "__op_storage__" not in members:
        m = Typed(sortedmap, ())
        m.set_name("__op_storage__")
        m.set_index(len(members))
        members["__op_storage__"] = m
Ejemplo n.º 2
0
class WxDateSelector(WxBoundedDate, ProxyDateSelector):
    """ A Wx implementation of an Enaml ProxyDateSelector.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(wx.DatePickerCtrl)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the wx.DatePickerCtrl widget.

        """
        self.widget = wx.DatePickerCtrl(self.parent_widget())

    def init_widget(self):
        """ Initialize the widget.

        """
        super(WxDateSelector, self).init_widget()
        d = self.declaration
        self.set_date_format(d.date_format)
        self.set_calendar_popup(d.calendar_popup)
        self.widget.Bind(wx.EVT_DATE_CHANGED, self.on_date_changed)

    #--------------------------------------------------------------------------
    # Abstract API Implementation
    #--------------------------------------------------------------------------
    def get_date(self):
        """ Return the current date in the control.

        Returns
        -------
        result : date
            The current control date as a date object.

        """
        return as_py_date(self.widget.GetValue())

    def set_minimum(self, date):
        """ Set the widget's minimum date.

        Parameters
        ----------
        date : date
            The date object to use for setting the minimum date.

        """
        widget = self.widget
        widget.SetRange(as_wx_date(date), widget.GetUpperLimit())

    def set_maximum(self, date):
        """ Set the widget's maximum date.

        Parameters
        ----------
        date : date
            The date object to use for setting the maximum date.

        """
        widget = self.widget
        widget.SetRange(widget.GetLowerLimit(), as_wx_date(date))

    def set_date(self, date):
        """ Set the widget's current date.

        Parameters
        ----------
        date : date
            The date object to use for setting the date.

        """
        self._guard |= CHANGED_GUARD
        try:
            self.widget.SetValue(as_wx_date(date))
        finally:
            self._guard &= ~CHANGED_GUARD

    def set_date_format(self, format):
        """ Set the widget's date format.

        Parameters
        ----------
        format : str
            A Python time formatting string.

        .. note:: Changing the format on wx is not supported.
                  See http://trac.wxwidgets.org/ticket/10988

        """
        pass

    def set_calendar_popup(self, popup):
        """ This is not supported on Wx.

        """
        pass
Ejemplo n.º 3
0
class Season(FormulaModel):
    season = Coerced(int)

    races = Property()
    _races = Typed(Races)

    drivers = Property()
    _drivers = Typed(Drivers)

    constructors = Property()
    _constructors = Typed(Constructors)

    def _get_races(self):
        if not self._races:
            self.races = self.api.races(year=self.season)
        return self._races

    def _set_races(self, races):
        if not isinstance(races, Races):
            self._races = Races(races)
        else:
            self._races = races

    def _get_drivers(self):
        if not self._drivers:
            self.drivers = self.api.query(year=self.season,
                                          query_type='drivers')
        return self._drivers

    def _set_drivers(self, drivers):
        if not isinstance(drivers, Drivers):
            self._drivers = Drivers(drivers)
        else:
            self._drivers = drivers

    def _get_constructors(self):
        if not self._constructors:
            self.constructors = self.api.query(year=self.season,
                                               query_type='constructors')
        return self._constructors

    def _set_constructors(self, constructors):
        if not isinstance(constructors, Drivers):
            self._constructors = Drivers(constructors)
        else:
            self._constructors = constructors

    @classmethod
    def from_dict(cls, kwargs):
        if 'races' in kwargs.keys():
            kwargs['races'] = [
                Race.from_dict(race) for race in kwargs.pop('races')
            ]
        return cls(**kwargs)

    def to_rows(self):

        if self.races:
            rows = []
            for race in self.races:
                race_row = race.to_row()
                race_row['season'] = self.season
                rows.append(race_row)
        else:
            rows = {'season': self.season}
        return rows

    def to_row(self):
        return {'season': self.season}

    def to_df(self):
        return pd.DataFrame(self.to_rows())

    def __repr__(self):
        return self.__id__

    @property
    def __id__(self):
        return 's' + str(self.season)
Ejemplo n.º 4
0
class QtDockItem(QtWidget, ProxyDockItem):
    """ A Qt implementation of an Enaml ProxyDockItem.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QCustomDockItem)

    #: Cyclic notification guard. This a bitfield of multiple guards.
    _guard = Int(0)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying QDockItem widget.

        """
        self.widget = QCustomDockItem(self.parent_widget())

    def init_widget(self):
        """ Initialize the state of the underlying widget.

        """
        super(QtDockItem, self).init_widget()
        d = self.declaration
        self.set_title(d.title)
        self.set_title_editable(d.title_editable)
        if not d.title_bar_visible:
            self.set_title_bar_visible(d.title_bar_visible)
        if d.icon is not None:
            self.set_icon(d.icon)
        if -1 not in d.icon_size:
            self.set_icon_size(d.icon_size)
        self.set_stretch(d.stretch)
        self.set_closable(d.closable)

    def init_layout(self):
        """ Initialize the layout for the underyling widget.

        """
        super(QtDockItem, self).init_layout()
        widget = self.widget
        widget.setDockWidget(self.dock_widget())
        # Use a queued connection so the dock manager can finish
        # closing the dock item before the signal handler runs.
        widget.titleEdited.connect(self.on_title_edited)
        widget.titleBarRightClicked.connect(self.on_title_bar_right_clicked)
        widget.closed.connect(self.on_closed, Qt.QueuedConnection)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def dock_widget(self):
        """ Find and return the dock widget child for this widget.

        """
        d = self.declaration.dock_widget()
        if d is not None:
            return d.proxy.widget

    #--------------------------------------------------------------------------
    # Reimplementations
    #--------------------------------------------------------------------------
    def refresh_style_sheet(self):
        """ A reimplemented styling method.

        The dock item uses custom stylesheet processing.

        """
        parts = []
        name = self.widget.objectName()
        for style in StyleCache.styles(self.declaration):
            t = translate_dock_item_style(name, style)
            if t:
                parts.append(t)
        if len(parts) > 0:
            stylesheet = '\n\n'.join(parts)
        else:
            stylesheet = ''
        self.widget.setStyleSheet(stylesheet)

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def on_title_edited(self, text):
        """ Handle the 'titleEdited' signal on the dock item.

        """
        d = self.declaration
        if d is not None:
            self._guard |= TITLE_GUARD
            try:
                d.title = text
            finally:
                self._guard &= ~TITLE_GUARD

    def on_title_bar_right_clicked(self, pos):
        """ Handle the 'titleBarRightClicked' signal on the dock item.

        """
        d = self.declaration
        if d is not None:
            d.title_bar_right_clicked()

    def on_closed(self):
        """ Handle the closed signal from the dock item.

        """
        d = self.declaration
        if d is not None:
            d._item_closed()

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def child_added(self, child):
        """ Handle the child added event for a QtDockItem.

        """
        super(QtDockItem, self).child_added(child)
        self.widget.setDockWidget(self.dock_widget())

    def child_removed(self, child):
        """ Handle the child added event for a QtDockItem.

        """
        super(QtDockItem, self).child_removed(child)
        self.widget.setDockWidget(self.dock_widget())

    #--------------------------------------------------------------------------
    # ProxyDockItem API
    #--------------------------------------------------------------------------
    def set_title(self, title):
        """ Set the title on the underlying widget.

        """
        if not self._guard & TITLE_GUARD:
            self.widget.setTitle(title)

    def set_title_editable(self, editable):
        """ Set the title editable state on the underlying widget.

        """
        self.widget.setTitleEditable(editable)

    def set_title_bar_visible(self, visible):
        """ Set the visibility of the widget's title bar.

        """
        self.widget.setTitleBarForceHidden(not visible)

    def set_icon(self, icon):
        """ Set the icon on the underlying widget.

        """
        if icon:
            qicon = get_cached_qicon(icon)
        else:
            qicon = QIcon()
        self.widget.setIcon(qicon)

    def set_icon_size(self, size):
        """ Set the icon size on the underlying widget.

        """
        self.widget.setIconSize(QSize(*size))

    def set_stretch(self, stretch):
        """ Set the stretch factor for the underlyling widget.

        """
        sp = self.widget.sizePolicy()
        sp.setHorizontalStretch(stretch)
        sp.setVerticalStretch(stretch)
        self.widget.setSizePolicy(sp)

    def set_closable(self, closable):
        """ Set the closable flag for the underlying widget.

        """
        self.widget.setClosable(closable)

    def alert(self, level, on, off, repeat, persist):
        """ Set the alert level on the underlying widget.

        """
        self.widget.alert(level, on, off, repeat, persist)
Ejemplo n.º 5
0
class QtComboBox(QtControl, ProxyComboBox):
    """ A Qt implementation of an Enaml ComboBox.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QComboBox)

    #: Cyclic notification guard. This a bitfield of multiple guards.
    _guard = Int(0)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the QComboBox widget.

        """
        box = QComboBox(self.parent_widget())
        box.setInsertPolicy(QComboBox.NoInsert)
        self.widget = box

    def init_widget(self):
        """ Create and initialize the underlying widget.

        """
        super(QtComboBox, self).init_widget()
        d = self.declaration
        self.set_items(d.items)
        self.set_index(d.index)
        self.set_editable(d.editable)
        self.widget.currentIndexChanged.connect(self.on_index_changed)

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def on_index_changed(self):
        """ The signal handler for the index changed signal.

        """
        if not self._guard & INDEX_GUARD:
            self.declaration.index = self.widget.currentIndex()

    #--------------------------------------------------------------------------
    # ProxyComboBox API
    #--------------------------------------------------------------------------
    def set_items(self, items):
        """ Set the items of the ComboBox.

        """
        widget = self.widget
        count = widget.count()
        nitems = len(items)
        for idx, item in enumerate(items[:count]):
            widget.setItemText(idx, item)
        if nitems > count:
            for item in items[count:]:
                widget.addItem(item)
        elif nitems < count:
            for idx in reversed(range(nitems, count)):
                widget.removeItem(idx)

    def set_index(self, index):
        """ Set the current index of the ComboBox.

        """
        self._guard |= INDEX_GUARD
        try:
            self.widget.setCurrentIndex(index)
        finally:
            self._guard &= ~INDEX_GUARD

    def set_editable(self, editable):
        """ Set whether the combo box is editable.

        """
        # The update is needed to avoid artificats (at least on Windows)
        widget = self.widget
        widget.setEditable(editable)
        widget.update()
Ejemplo n.º 6
0
class QtLabel(QtControl, ProxyLabel):
    """ A Qt implementation of an Enaml ProxyLabel.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QLabel)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying label widget.

        """
        self.widget = QLabel(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying widget.

        """
        super(QtLabel, self).init_widget()
        d = self.declaration
        self.set_text(d.text)
        self.set_align(d.align)
        self.set_vertical_align(d.vertical_align)
        self.widget.linkActivated.connect(self.on_link_activated)

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def on_link_activated(self, link):
        """ Handle the link activated signal.

        """
        self.declaration.link_activated(link)

    #--------------------------------------------------------------------------
    # ProxyLabel API
    #--------------------------------------------------------------------------
    def set_text(self, text):
        """ Set the text in the widget.

        """
        with self.geometry_guard():
            self.widget.setText(text)

    def set_align(self, align):
        """ Set the alignment of the text in the widget.

        """
        widget = self.widget
        alignment = widget.alignment()
        alignment &= ~Qt.AlignHorizontal_Mask
        alignment |= ALIGN_MAP[align]
        widget.setAlignment(alignment)

    def set_vertical_align(self, align):
        """ Set the vertical alignment of the text in the widget.

        """
        widget = self.widget
        alignment = widget.alignment()
        alignment &= ~Qt.AlignVertical_Mask
        alignment |= VERTICAL_ALIGN_MAP[align]
        widget.setAlignment(alignment)
Ejemplo n.º 7
0
class QtMenu(QtToolkitObject, ProxyMenu):
    """ A Qt implementation of an Enaml ProxyMenu.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QCustomMenu)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying menu widget.

        """
        self.widget = QCustomMenu(self.parent_widget())

    def init_widget(self):
        """ Initialize the widget.

        """
        super(QtMenu, self).init_widget()
        d = self.declaration
        self.set_title(d.title)
        self.set_enabled(d.enabled)
        self.set_visible(d.visible)
        self.set_context_menu(d.context_menu)

    def init_layout(self):
        """ Initialize the layout of the widget.

        """
        super(QtMenu, self).init_layout()
        widget = self.widget
        for child in self.children():
            if isinstance(child, QtMenu):
                widget.addMenu(child.widget)
            elif isinstance(child, QtAction):
                widget.addAction(child.widget)
            elif isinstance(child, QtActionGroup):
                widget.addActions(child.actions())

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def find_next_action(self, child):
        """ Get the QAction instance which follows the child.

        Parameters
        ----------
        child : QtToolkitObject
            The child of interest.

        Returns
        -------
        result : QAction or None
            The QAction which comes immediately after the actions of the
            given child, or None if no actions follow the child.

        """
        found = False
        for dchild in self.children():
            if found:
                if isinstance(dchild, QtMenu):
                    return dchild.widget.menuAction()
                if isinstance(dchild, QtAction):
                    return dchild.widget
                if isinstance(dchild, QtActionGroup):
                    acts = dchild.actions()
                    if len(acts) > 0:
                        return acts[0]
            else:
                found = dchild is child

    def child_added(self, child):
        """ Handle the child added event for a QtMenu.

        """
        super(QtMenu, self).child_added(child)
        if isinstance(child, QtMenu):
            before = self.find_next_action(child)
            self.widget.insertMenu(before, child.widget)
        elif isinstance(child, QtAction):
            before = self.find_next_action(child)
            self.widget.insertAction(before, child.widget)
        elif isinstance(child, QtActionGroup):
            before = self.find_next_action(child)
            self.widget.insertActions(before, child.actions())

    def child_removed(self, child):
        """  Handle the child removed event for a QtMenu.

        """
        super(QtMenu, self).child_removed(child)
        if isinstance(child, QtMenu):
            self.widget.removeAction(child.widget.menuAction())
        elif isinstance(child, QtAction):
            self.widget.removeAction(child.widget)
        elif isinstance(child, QtActionGroup):
            self.widget.removeActions(child.actions())

    #--------------------------------------------------------------------------
    # ProxyMenu API
    #--------------------------------------------------------------------------
    def set_title(self, title):
        """ Set the title of the underlying widget.

        """
        self.widget.setTitle(title)

    def set_visible(self, visible):
        """ Set the visibility on the underlying widget.

        """
        self.widget.menuAction().setVisible(visible)

    def set_enabled(self, enabled):
        """ Set the enabled state of the widget.

        """
        self.widget.setEnabled(enabled)

    def set_context_menu(self, context):
        """ Set whether or not the menu is a context menu.

        """
        self.widget.setContextMenu(context)

    def popup(self):
        """ Popup the menu over the current mouse location.

        """
        self.widget.exec_(QCursor.pos())
Ejemplo n.º 8
0
class QtStack(QtConstraintsWidget, ProxyStack):
    """ A Qt implementation of an Enaml Stack.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QStack)

    #: Cyclic notification guards
    _guard = Int(0)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying QStack widget.

        """
        self.widget = QStack(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying control.

        """
        super(QtStack, self).init_widget()
        d = self.declaration
        self.set_transition(d.transition)
        self.set_size_hint_mode(d.size_hint_mode, update=False)

    def init_layout(self):
        """ Initialize the layout of the underlying control.

        """
        super(QtStack, self).init_layout()
        widget = self.widget
        for item in self.stack_items():
            widget.addWidget(item)
        # Bypass the transition effect during initialization.
        widget.setCurrentIndex(self.declaration.index)
        widget.layoutRequested.connect(self.on_layout_requested)
        widget.currentChanged.connect(self.on_current_changed)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def stack_items(self):
        """ Get the stack items defined on the control.

        """
        for d in self.declaration.stack_items():
            w = d.proxy.widget
            if w is not None:
                yield w

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def child_added(self, child):
        """ Handle the child added event for a QtStack.

        """
        super(QtStack, self).child_added(child)
        if isinstance(child, QtStackItem):
            for index, dchild in enumerate(self.children()):
                if child is dchild:
                    self.widget.insertWidget(index, child.widget)

    def child_removed(self, child):
        """ Handle the child removed event for a QtStack.

        """
        super(QtStack, self).child_removed(child)
        if isinstance(child, QtStackItem):
            self.widget.removeWidget(child.widget)

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def on_layout_requested(self):
        """ Handle the `layoutRequested` signal from the QStack.

        """
        self.geometry_updated()

    def on_current_changed(self):
        """ Handle the `currentChanged` signal from the QStack.

        """
        if not self._guard & INDEX_FLAG:
            self._guard |= INDEX_FLAG
            try:
                self.declaration.index = self.widget.currentIndex()
            finally:
                self._guard &= ~INDEX_FLAG

    #--------------------------------------------------------------------------
    # Widget Update Methods
    #--------------------------------------------------------------------------
    def set_index(self, index):
        """ Set the current index of the underlying widget.

        """
        if not self._guard & INDEX_FLAG:
            self._guard |= INDEX_FLAG
            try:
                self.widget.transitionTo(index)
            finally:
                self._guard &= ~INDEX_FLAG

    def set_transition(self, transition):
        """ Set the transition on the underlying widget.

        """
        if transition:
            self.widget.setTransition(make_transition(transition))
        else:
            self.widget.setTransition(None)

    def set_size_hint_mode(self, mode, update=True):
        """ Set the size hint mode for the widget.

        """
        self.widget.setSizeHintMode(SIZE_HINT_MODE[mode])
        if update:
            self.geometry_updated()
Ejemplo n.º 9
0
class QtColorDialog(QtToolkitDialog, ProxyColorDialog):
    """ A Qt implementation of an Enaml ProxyColorDialog.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QColorDialogEx)

    #: Cyclic notification guard. This a bitfield of multiple guards.
    _guard = Int(0)

    def create_widget(self):
        """ Create the underlying QColorDialog.

        """
        self.widget = QColorDialogEx(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying widget.

        """
        super(QtColorDialog, self).init_widget()
        d = self.declaration
        self.set_current_color(d.current_color)
        self.set_show_alpha(d.show_alpha)
        self.set_show_buttons(d.show_buttons)
        widget = self.widget
        widget.currentColorChanged.connect(self.on_current_color_changed)
        widget.colorSelected.connect(self.on_color_selected)
        # use the custom finished signal instead of the superclass'
        widget.finished.disconnect(self.on_finished)
        widget.reallyFinished.connect(self.on_finished)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def get_default_title(self):
        """ Get the default window title for the color dialog.

        """
        return u'Select Color'

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def on_current_color_changed(self, qcolor):
        """ Handle the 'currentColorChanged' signal from the widget.

        """
        d = self.declaration
        if d is not None:
            self._guard |= CURRENT_GUARD
            try:
                d.current_color = color_from_qcolor(qcolor)
            finally:
                self._guard &= ~CURRENT_GUARD

    def on_color_selected(self, qcolor):
        """ Handle the 'colorSelected' signal from the widget.

        """
        d = self.declaration
        if d is not None:
            d.selected_color = color_from_qcolor(qcolor)

    #--------------------------------------------------------------------------
    # ProxyColorDialog API
    #--------------------------------------------------------------------------
    @staticmethod
    def custom_count():
        """ Get the number of available custom colors.

        """
        return QColorDialog.customCount()

    @staticmethod
    def custom_color(index):
        """ Get the custom color for the given index.

        """
        qrgb = QColorDialog.customColor(index)
        return color_from_qcolor(QColor.fromRgba(qrgb))

    @staticmethod
    def set_custom_color(index, color):
        """ Set the custom color for the given index.

        """
        QColorDialog.setCustomColor(index, color.argb)

    def set_current_color(self, color):
        """ Set the current color for the underlying widget.

        """
        if not self._guard & CURRENT_GUARD:
            if color is not None:
                qcolor = QColor.fromRgba(color.argb)
            else:
                qcolor = QColor()
            self.widget.setCurrentColor(qcolor)

    def set_show_alpha(self, show):
        """ Set the show alpha option on the underlying widget.

        """
        widget = self.widget
        opt = widget.options()
        if show:
            opt |= QColorDialog.ShowAlphaChannel
        else:
            opt &= ~QColorDialog.ShowAlphaChannel
        widget.setOptions(opt)

    def set_show_buttons(self, show):
        """ Set the show buttons option on the underlying widget.

        """
        widget = self.widget
        opt = widget.options()
        if show:
            opt &= ~QColorDialog.NoButtons
        else:
            opt |= QColorDialog.NoButtons
        widget.setOptions(opt)
Ejemplo n.º 10
0
class WxMenuBar(WxToolkitObject, ProxyMenuBar):
    """ A Wx implementation of an Enaml ProxyMenuBar.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(wxMenuBar)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying menu bar widget.

        """
        # Wx behaves better when creating the menu bar without a parent.
        self.widget = wxMenuBar()

    def init_layout(self):
        """ Initialize the layout for the menu bar.

        """
        super(WxMenuBar, self).init_layout()
        widget = self.widget
        for child in self.children():
            if isinstance(child, WxMenu):
                widget.AddMenu(child.widget)

    def destroy(self):
        """ A reimplemented destructor.

        This destructor simply drops the reference to the menu bar and
        the enaml declaration and clears the menus in the menu bar.
        Destroying it will cause wx to segfault.

        """
        if self.widget:
            self.widget.SetMenus([])
        del self.widget
        del self.declaration

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def find_next_menu(self, child):
        """ Get the wxMenu instance which follows the child.

        Parameters
        ----------
        child : WxMenu
            The child menu of interest.

        Returns
        -------
        result : wxMenu or None
            The wxMenu which comes immediately after the actions of the
            given child, or None if no actions follow the child.

        """
        found = False
        for dchild in self.children():
            if found:
                if isinstance(dchild, WxMenu):
                    return dchild.widget
            else:
                found = dchild is child

    def child_added(self, child):
        """ Handle the child added event for a WxMenuBar.

        """
        super(WxMenuBar, self).child_added(child)
        if isinstance(child, WxMenu):
            before = self.find_next_menu(child)
            self.widget.InsertMenu(before, child.widget)

    def child_removed(self, child):
        """ Handle the child removed event for a WxMenuBar.

        """
        super(WxMenuBar, self).child_removed(child)
        if isinstance(child, WxMenu):
            self.widget.RemoveMenu(child.widget)
Ejemplo n.º 11
0
class QtDockArea(QtConstraintsWidget, ProxyDockArea):
    """ A Qt implementation of an Enaml DockArea.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QDockArea)

    #: The docking manager which will drive the dock area.
    manager = Typed(DockManager)

    #: The event filter which listens for layout requests.
    dock_layout_filter = Typed(DockLayoutFilter)

    #: The event filter which listens for dock events.
    dock_event_filter = Typed(DockEventFilter)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying QDockArea widget.

        """
        self.widget = QDockArea(self.parent_widget())
        self.manager = DockManager(self.widget)
        self.dock_event_filter = DockEventFilter(self)
        self.dock_layout_filter = DockLayoutFilter(self)

    def init_widget(self):
        """ Initialize the underlying widget.

        """
        super(QtDockArea, self).init_widget()
        d = self.declaration
        self.set_tab_position(d.tab_position)
        self.set_live_drag(d.live_drag)
        if d.style:  # TODO remove this in Enaml 1.0
            self.set_style(d.style)
        self.set_dock_events_enabled(d.dock_events_enabled)

    def init_layout(self):
        """ Initialize the layout of the underlying control.

        """
        super(QtDockArea, self).init_layout()
        manager = self.manager
        for item in self.dock_items():
            manager.add_item(item)
        d = self.declaration
        self.apply_layout(d.layout)
        self.widget.installEventFilter(self.dock_layout_filter)

    def destroy(self):
        """ A reimplemented destructor.

        This removes the event filter from the dock area and releases
        the items from the dock manager.

        """
        self.widget.removeEventFilter(self.dock_layout_filter)
        self.widget.removeEventFilter(self.dock_event_filter)
        del self.dock_layout_filter
        del self.dock_event_filter
        self.manager.destroy()
        super(QtDockArea, self).destroy()

    #--------------------------------------------------------------------------
    # Overrides
    #--------------------------------------------------------------------------
    def refresh_style_sheet(self):
        """ A reimplemented styling method.

        The dock area uses custom stylesheet processing.

        """
        # workaround win-7 sizing bug
        parts = [u'QDockTabWidget::pane {}']
        name = self.widget.objectName()
        for style in StyleCache.styles(self.declaration):
            t = translate_dock_area_style(name, style)
            if t:
                parts.append(t)
        if len(parts) > 1:
            stylesheet = u'\n\n'.join(parts)
        else:
            stylesheet = u''
        self.widget.setStyleSheet(stylesheet)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def dock_items(self):
        """ Get an iterable of QDockItem children for this area.

        """
        for d in self.declaration.dock_items():
            w = d.proxy.widget
            if w is not None:
                yield w

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def child_added(self, child):
        """ Handle the child added event for a QtDockArea.

        """
        super(QtDockArea, self).child_added(child)
        if isinstance(child, QtDockItem):
            w = child.widget
            if w is not None:
                self.manager.add_item(w)

    def child_removed(self, child):
        """ Handle the child removed event for a QtDockArea.

        """
        super(QtDockArea, self).child_removed(child)
        if isinstance(child, QtDockItem):
            w = child.widget
            if w is not None:
                self.manager.remove_item(w)

    #--------------------------------------------------------------------------
    # ProxyDockArea API
    #--------------------------------------------------------------------------
    def set_tab_position(self, position):
        """ Set the default tab position on the underyling widget.

        """
        self.widget.setTabPosition(TAB_POSITIONS[position])

    def set_live_drag(self, live_drag):
        """ Set the live drag state for the underlying widget.

        """
        self.widget.setOpaqueItemResize(live_drag)

    def set_style(self, style):
        """ Set the style for the underlying widget.

        """
        # If get_style_sheet returns something, it means the user will
        # have already called register_style_sheet, which will raise
        # a deprecation warning. TODO remove this method in Enaml 1.0.
        sheet = get_style_sheet(style)
        if sheet:
            self.widget.setStyleSheet(sheet)

    def set_dock_events_enabled(self, enabled):
        """ Set whether or not dock events are enabled for the area.

        """
        widget = self.widget
        widget.setDockEventsEnabled(enabled)
        if enabled:
            widget.installEventFilter(self.dock_event_filter)
        else:
            widget.removeEventFilter(self.dock_event_filter)

    def save_layout(self):
        """ Save the current layout on the underlying widget.

        """
        return self.manager.save_layout()

    def apply_layout(self, layout):
        """ Apply a new layout to the underlying widget.

        """
        self.manager.apply_layout(layout)

    def update_layout(self, ops):
        """ Update the layout from a list of layout operations.

        """
        self.manager.update_layout(ops)
Ejemplo n.º 12
0
class IconManagerPlugin(HasPreferencesPlugin):
    """Plugin managing icon theme and access to icon for the application.

    """
    #: Id of the currently selected icon theme
    current_theme = Str('exopy.FontAwesome').tag(pref=True)

    #: Id of the icon theme to use as fallback if a theme fail to provide an
    #: icon.
    fallback_theme = Str('exopy.FontAwesome').tag(pref=True)

    #: Registered icon themes ids
    icon_themes = List()

    def start(self):
        """Start the plugin lifecycle and collect themes and extensions.

        """
        super(IconManagerPlugin, self).start()

        checker = make_extension_validator(IconTheme, (), ())
        self._icon_themes = ExtensionsCollector(workbench=self.workbench,
                                                point=ICON_THEME_POINT,
                                                ext_class=IconTheme,
                                                validate_ext=checker)
        self._icon_themes.start()
        self._list_icon_themes()

        if self.current_theme not in self.icon_themes:
            self.current_theme = self.icon_themes[0]

        if self.fallback_theme not in self.icon_themes:
            self.fallback_theme = 'exopy.FontAwesome'

        checker = make_extension_validator(IconThemeExtension, (), ('theme', ))
        self._icon_theme_extensions = \
            ExtensionsCollector(workbench=self.workbench,
                                point=ICON_THEME_EXTENSION_POINT,
                                ext_class=IconThemeExtension,
                                validate_ext=checker)
        self._icon_theme_extensions.start()
        self._add_extensions_to_selected_theme()

        self._bind_observers()

    def stop(self):
        """Stop the plugin and clean up.

        """
        self._unbind_observers()
        self._icon_theme_extensions.stop()
        self._icon_themes.stop()

    def get_icon(self, icon_id):
        """Get an icon from the selected theme.

        Fallback to fallback_theme if no matching icon is found in the selected
        theme.

        """
        icon_theme = self._icon_themes.contributions[self.current_theme]
        icon = None
        msg = ''
        try:
            icon = icon_theme.get_icon(self, icon_id)
        except Exception:
            msg = 'Icon theme %s failed to provide icon %s and raised:\n%s'
            msg = msg % (self.current_theme, icon_id, format_exc())
        else:
            if icon is None:
                msg = 'Icon theme %s failed to provide icon %s without errors.'
                msg = msg % (self.current_theme, icon_id)

        if msg:
            fallback = self._icon_themes.contributions[self.fallback_theme]
            try:
                icon = fallback.get_icon(self, icon_id)
            except Exception:
                msg += ('Fallback theme %s failed to provide icon %s and '
                        'raised:\n%s')
                msg = msg % (self.fallback_theme, icon_id, format_exc())
            else:
                if icon is None:
                    msg += ('Fallback theme %s failed to provide icon %s '
                            'without errors.')
                    msg = msg % (self.fallback_theme, icon_id)

            logger = logging.getLogger(__name__)
            logger.warning(msg)

        return icon

    # --- Private API ---------------------------------------------------------

    #: Collector for the declared icon themes.
    _icon_themes = Typed(ExtensionsCollector)

    #: Collector for the declared icon theme extensions.
    _icon_theme_extensions = Typed(ExtensionsCollector)

    #: Currently selected theme.
    _current_theme = Typed(IconTheme)

    def _add_extensions_to_selected_theme(self, change=None):
        """Add contributed theme extension to the selected theme.

        """
        selected = self._current_theme

        # Assign all contributed icons from all extensions.
        if change is None:
            for k, v in self._icon_theme_extensions.contributions.items():
                if v.theme == selected.id:
                    selected.insert_children(None, v.icons())

        # Only update icons provided by new extensions.
        else:
            added = set(change['value']) - set(change.get('oldvalue', {}))
            removed = set(change.get('oldvalue', {})) - set(change['value'])
            ext = dict(change['value'])
            ext.update(change.get('oldvalue', {}))
            for k in added:
                v = ext[k]
                if v.theme == selected.id:
                    selected.insert_children(None, v.icons())
            for k in removed:
                v = ext[k]
                if v.theme == selected.id:
                    v.insert_children(None, v.icons())

        del selected._icons

    def _post_setattr_current_theme(self, old, new):
        """Add the extension icons to the theme.

        """
        del self._current_theme
        if self._icon_theme_extensions:
            self._add_extensions_to_selected_theme()

    def _list_icon_themes(self, change=None):
        """List the declared icon themes.

        """
        self.icon_themes = sorted(self._icon_themes.contributions)

    def _bind_observers(self):
        """Setup the observers on the contributions.

        """
        self._icon_themes.observe('contributions', self._list_icon_themes)
        callback = self._add_extensions_to_selected_theme
        self._icon_theme_extensions.observe('contributions', callback)

    def _unbind_observers(self):
        """Remove the observers on the contributions.

        """
        self._icon_themes.unobserve('contributions', self._list_icon_themes)
        callback = self._add_extensions_to_selected_theme
        self._icon_theme_extensions.unobserve('contributions', callback)

    def _default__current_theme(self):
        """Get the current theme object based on the current_theme member.

        """
        return self._icon_themes.contributions[self.current_theme]
Ejemplo n.º 13
0
class OccShape(ProxyShape):
    #: A reference to the toolkit shape created by the proxy.
    shape = Typed(TopoDS_Shape)

    #: The shape that was shown on the screen
    ais_shape = Instance(AIS_Shape)

    #: Topology explorer of the shape
    topology = Typed(Topology)

    #: Class reference url
    reference = Str()

    # -------------------------------------------------------------------------
    # Initialization API
    # -------------------------------------------------------------------------
    def create_shape(self):
        """ Create the toolkit shape for the proxy object.

        This method is called during the top-down pass, just before the
        'init_shape()' method is called. This method should create the
        toolkit widget and assign it to the 'widget' attribute.

        """
        raise NotImplementedError

    def init_shape(self):
        """ Initialize the state of the toolkit widget.

        This method is called during the top-down pass, just after the
        'create_widget()' method is called. This method should init the
        state of the widget. The child widgets will not yet be created.

        """
        pass

    def init_layout(self):
        """ Initialize the layout of the toolkit shape.

        """
        pass

    def activate_top_down(self):
        """ Activate the proxy for the top-down pass.

        """
        #log.debug(f"{self}.create_shape()")
        self.create_shape()
        #log.debug(f"{self}.init_shape()")
        self.init_shape()

    def activate_bottom_up(self):
        """ Activate the proxy tree for the bottom-up pass.

        """
        #log.debug(f"{self}.init_layout()")
        self.init_layout()

    # -------------------------------------------------------------------------
    # Defaults and Observers
    # -------------------------------------------------------------------------
    def _default_topology(self):
        if self.shape is None:
            self.create_shape()
        return Topology(shape=self.shape)

    @observe('shape')
    def update_topology(self, change):
        if self.shape is not None:
            self.topology = self._default_topology()

    #@observe('shape')
    #def update_display(self, change):
    #    parent = self.parent()
    #    if parent:
    #        parent.update_display(change)

    def get_first_child(self):
        """ Return shape to apply the operation to. """
        for child in self.children():
            if isinstance(child, OccShape):
                return child

    def child_shapes(self):
        """ Iterator of all child shapes """
        for child in self.children():
            if isinstance(child, OccShape):
                if hasattr(child, 'shapes'):
                    for s in child.shapes:
                        yield s
                else:
                    yield child.shape

    # -------------------------------------------------------------------------
    # Proxy API
    # -------------------------------------------------------------------------
    def get_transform(self):
        """ Create a transform which rotates the default axis to align
        with the normal given by the position

        Returns
        -------
        transform: gp_Trsf

        """
        d = self.declaration

        # Move to position and align along direction axis
        t = gp_Trsf()
        if d.direction.is_parallel(DZ):
            t.SetRotation(AZ, d.direction.angle(DZ) + d.rotation)
        else:
            d1 = d.direction.cross(DZ)
            axis = gp_Ax1(gp_Pnt(0, 0, 0), d1.proxy)
            t.SetRotation(axis, d.direction.angle(DZ))

            # Apply the rotation an reverse any rotation added in
            sign = 1 if d1.y >= 0 else -1
            angle = d.rotation + sign * d1.angle(DX)

            if angle:
                rot = gp_Trsf()
                rot.SetRotation(AZ, angle)
                t.Multiply(rot)

        t.SetTranslationPart(gp_Vec(*d.position))
        return t

    def set_direction(self, direction):
        self.create_shape()

    def set_axis(self, axis):
        self.create_shape()

    def parent_shape(self):
        return self.parent().shape

    def get_bounding_box(self, shape=None):
        shape = shape or self.shape
        if not shape:
            return BBox()
        bbox = Bnd_Box()
        BRepBndLib.Add_(shape, bbox)
        pmin, pmax = bbox.CornerMin(), bbox.CornerMax()
        return BBox(*(pmin.X(), pmin.Y(), pmin.Z(), pmax.X(), pmax.Y(),
                      pmax.Z()))
Ejemplo n.º 14
0
class QtToolkitDialog(QtToolkitObject, ProxyToolkitDialog):
    """ A Qt implementation of an Enaml ProxyToolkitDialog.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QDialog)

    def create_widget(self):
        """ Create the underlying QColorDialog.

        """
        self.widget = QDialog(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying widget.

        """
        super(QtToolkitDialog, self).init_widget()
        self.set_title(self.declaration.title)
        self.widget.finished.connect(self.on_finished)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def get_default_title(self):
        """ Get the default window title for the dialog.

        This can be reimplemented by subclass to provide a default
        window title. The base implementation returns an empty string.

        """
        return u''

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def on_finished(self, result):
        """ Handle the 'finished' signal from the widget.

        """
        d = self.declaration
        if d is not None:
            d._proxy_finished(bool(result))

    #--------------------------------------------------------------------------
    # ProxyToolkitDialog API
    #--------------------------------------------------------------------------
    def set_title(self, title):
        """ Set the window title for the underlying widget.

        """
        self.widget.setWindowTitle(title or self.get_default_title())

    def show(self):
        """ Open the dialog as non modal.

        """
        self.widget.show()

    def open(self):
        """ Open the dialog as window modal.

        """
        self.widget.open()

    def exec_(self):
        """ Open the dialog as application modal.

        """
        self.widget.exec_()

    def accept(self):
        """ Accept the current state and close the dialog.

        """
        self.widget.accept()

    def reject(self):
        """ Reject the current state and close the dialog.

        """
        self.widget.reject()
Ejemplo n.º 15
0
class QtActionGroup(QtToolkitObject, ProxyActionGroup):
    """ A Qt implementation of an Enaml ProxyActionGroup.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QCustomActionGroup)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying action group widget.

        """
        self.widget = QCustomActionGroup(self.parent_widget())

    def init_widget(self):
        """ Initialize the control.

        """
        super(QtActionGroup, self).init_widget()
        d = self.declaration
        self.set_exclusive(d.exclusive)
        self.set_enabled(d.enabled)
        self.set_visible(d.visible)

    def init_layout(self):
        """ Initialize the layout for the control.

        """
        super(QtActionGroup, self).init_layout()
        widget = self.widget
        for action in self.actions():
            widget.addAction(action)

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def find_next_action(self, child):
        """ Locate the QAction object which logically follows the child.

        If the given child is last in the list of children, then the
        parent object will be invoked to find the QAction which follows
        this action group.

        Parameters
        ----------
        child : QtToolkitObject
            The child object of interest.

        Returns
        -------
        result : QAction or None
            The QAction which logically follows the position of the
            child in the list of children. None will be returned if
            a relevant QAction is not found.

        """
        found = False
        for dchild in self.children():
            if found and isinstance(dchild, QtAction):
                return dchild.widget
            else:
                found = child is dchild
        parent = self.parent()
        if parent is not None:
            return parent.find_next_action(self)

    def child_added(self, child):
        """ Handle the child added event for a QtActionGroup.

        This handler will also add the widget to the parent widget,
        since a QActionGroup only serves as a management container.

        """
        super(QtActionGroup, self).child_added(child)
        if isinstance(child, QtAction):
            self.widget.addAction(child.widget)
            parent = self.parent()
            if parent is not None:
                before = self.find_next_action(child)
                parent.widget.insertAction(before, child.widget)

    def child_removed(self, child):
        """ Handle the child removed event for a QtActionGroup.

        This handler will also remove the widget to the parent widget,
        since a QActionGroup only serves as a management container.

        """
        super(QtActionGroup, self).child_removed(child)
        if isinstance(child, QtAction) and child.widget is not None:
            self.widget.removeAction(child.widget)
            parent = self.parent()
            if parent is not None:
                parent.widget.removeAction(child.widget)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def actions(self):
        """ Get the QAction children for this action group.

        Returns
        -------
        result : list
            The list of QAction instances which are children of this
            action group. Unlike the list returned by the `actions`
            method of the QActionGroup, the children in this list will
            have the correct order.

        """
        isinst = isinstance
        return [c.widget for c in self.children() if isinst(c, QtAction)]

    #--------------------------------------------------------------------------
    # ProxyActionGroup API
    #--------------------------------------------------------------------------
    def set_exclusive(self, exclusive):
        """ Set the exclusive state of the underlying control.

        """
        self.widget.setExclusive(exclusive)

    def set_enabled(self, enabled):
        """ Set the enabled state of the underlying control.

        """
        self.widget.setEnabled(enabled)

    def set_visible(self, visible):
        """ Set the visible state of the underlying control.

        """
        self.widget.setVisible(visible)
Ejemplo n.º 16
0
class AndroidMapView(AndroidFrameLayout, ProxyMapView):
    """ An Android implementation of an Enaml ProxyMapView.

    """

    #: Holder
    widget = Typed(FrameLayout)

    #: A reference to the widget created by the proxy.
    fragment = Typed(MapFragment)

    #: Map options
    options = Typed(GoogleMapOptions)

    #: Map instance
    map = Typed(GoogleMap)

    #: TODO: Lookup table for markers
    markers = Dict()

    #: Camera updating
    _update_blocked = Bool()

    #: Info window adapter
    adapter = Typed(GoogleMap.InfoWindowAdapter)

    # -------------------------------------------------------------------------
    # Initialization API
    # -------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying widget.

        """
        self.init_options()

        #: Retrieve the actual map
        MapFragment.newInstance(self.options).then(
            self.on_map_fragment_created)

        # Holder for the fragment
        self.widget = FrameLayout(self.get_context())

        # I wrote this a few days ago and already forget how this hack works...
        # lol We can't simply get a map reference using getMapAsync in the
        # return value like we normally do with a normal call function return
        # value. The bridge design was modified to store an object that cannot
        # be decoded normally (via a standard Bridge.Packer) by saving the new
        # object in the cache returning the id of the handler or proxy that
        # invoked it. This way we can manually create a new id and pass that
        # "future reference-able" object as our listener. At which point the
        # bridge will create a reference entry in the cache for us with the of
        # the object we gave it. Once in the cache we can use it like any
        # bridge object we created.
        self.map = GoogleMap(__id__=bridge.generate_id())

    def init_options(self):
        """ Initialize the underlying map options.

        """
        self.options = GoogleMapOptions()
        d = self.declaration
        self.set_map_type(d.map_type)
        if d.ambient_mode:
            self.set_ambient_mode(d.ambient_mode)
        if (d.camera_position or d.camera_zoom or d.camera_tilt
                or d.camera_bearing):
            self.update_camera()
        if d.map_bounds:
            self.set_map_bounds(d.map_bounds)
        if not d.show_compass:
            self.set_show_compass(d.show_compass)
        if not d.show_zoom_controls:
            self.set_show_zoom_controls(d.show_zoom_controls)
        if not d.show_toolbar:
            self.set_show_toolbar(d.show_toolbar)
        if d.lite_mode:
            self.set_lite_mode(d.lite_mode)
        if not d.rotate_gestures:
            self.set_rotate_gestures(d.rotate_gestures)
        if not d.scroll_gestures:
            self.set_scroll_gestures(d.scroll_gestures)
        if not d.tilt_gestures:
            self.set_tilt_gestures(d.tilt_gestures)
        if not d.zoom_gestures:
            self.set_zoom_gestures(d.zoom_gestures)
        if d.min_zoom:
            self.set_min_zoom(d.min_zoom)
        if d.max_zoom:
            self.set_max_zoom(d.max_zoom)

    def init_map(self):
        """ Add markers, polys, callouts, etc.."""
        d = self.declaration
        if d.show_location:
            self.set_show_location(d.show_location)
        if d.show_traffic:
            self.set_show_traffic(d.show_traffic)
        if d.show_indoors:
            self.set_show_indoors(d.show_indoors)
        if d.show_buildings:
            self.set_show_buildings(d.show_buildings)

        #: Local ref access is faster
        mapview = self.map
        mid = mapview.getId()

        #: Connect signals
        #: Camera
        mapview.onCameraChange.connect(self.on_camera_changed)
        mapview.onCameraMoveStarted.connect(self.on_camera_move_started)
        mapview.onCameraMoveCanceled.connect(self.on_camera_move_stopped)
        mapview.onCameraIdle.connect(self.on_camera_move_stopped)
        mapview.setOnCameraChangeListener(mid)
        mapview.setOnCameraMoveStartedListener(mid)
        mapview.setOnCameraMoveCanceledListener(mid)
        mapview.setOnCameraIdleListener(mid)

        #: Clicks
        mapview.onMapClick.connect(self.on_map_clicked)
        mapview.setOnMapClickListener(mid)
        mapview.onMapLongClick.connect(self.on_map_long_clicked)
        mapview.setOnMapLongClickListener(mid)

        #: Markers
        mapview.onMarkerClick.connect(self.on_marker_clicked)
        mapview.setOnMarkerClickListener(self.map.getId())
        mapview.onMarkerDragStart.connect(self.on_marker_drag_start)
        mapview.onMarkerDrag.connect(self.on_marker_drag)
        mapview.onMarkerDragEnd.connect(self.on_marker_drag_end)
        mapview.setOnMarkerDragListener(mid)

        #: Info window
        mapview.onInfoWindowClick.connect(self.on_info_window_clicked)
        mapview.onInfoWindowLongClick.connect(self.on_info_window_long_clicked)
        mapview.onInfoWindowClose.connect(self.on_info_window_closed)
        mapview.setOnInfoWindowClickListener(mid)
        mapview.setOnInfoWindowCloseListener(mid)
        mapview.setOnInfoWindowLongClickListener(mid)

        #: Polys
        mapview.onPolygonClick.connect(self.on_poly_clicked)
        mapview.onPolylineClick.connect(self.on_poly_clicked)
        mapview.setOnPolygonClickListener(mid)
        mapview.setOnPolylineClickListener(mid)

        #: Circle
        mapview.onCircleClick.connect(self.on_circle_clicked)
        mapview.setOnCircleClickListener(mid)

    def init_info_window_adapter(self):
        """ Initialize the info window adapter. Should only be done if one of 
        the markers defines a custom view.
        """
        adapter = self.adapter
        if adapter:
            return  #: Already initialized
        adapter = GoogleMap.InfoWindowAdapter()
        adapter.getInfoContents.connect(self.on_info_window_contents_requested)
        adapter.getInfoWindow.connect(self.on_info_window_requested)
        self.map.setInfoWindowAdapter(adapter)

    # -------------------------------------------------------------------------
    # Google Maps API
    # -------------------------------------------------------------------------
    def on_map_fragment_created(self, obj_id):
        """ Create the fragment and pull the map reference when it's loaded. 
        """
        self.fragment = MapFragment(__id__=obj_id)

        #: Setup callback so we know when the map is ready
        self.map.onMapReady.connect(self.on_map_ready)
        self.fragment.getMapAsync(self.map.getId())

        context = self.get_context()

        def on_transaction(id):
            trans = FragmentTransaction(__id__=id)
            trans.add(self.widget.getId(), self.fragment)
            trans.commit()

        def on_fragment_manager(id):
            fm = FragmentManager(__id__=id)
            fm.beginTransaction().then(on_transaction)

        context.widget.getSupportFragmentManager().then(on_fragment_manager)
        # #: Get GoogleMap instance when ready
        # #: Doesn't work...
        # def get_map(result):
        #     print("Maps initializer result: {}".format(result))
        #     if result==ConnectionResult.SUCCESS:
        #         self.fragment.onMapReady.connect(self.on_map_ready)
        #         self.fragment.getMapAsync(self.fragment.getId())
        #     else:
        #         app = self.get_context()
        #         app.show_error("Error getting map: {}".format(result))
        # MapsInitializer.initialize(self.get_context()).then(get_map)

    def on_map_ready(self, map_id):
        #: At this point the map is valid
        self.init_map()

        #: Reload markers
        for child in self.children():
            if isinstance(child, AndroidMapItemBase):
                child.add_to_map(self.map)

    def child_added(self, child):
        if isinstance(child, AndroidMapItemBase):
            child.add_to_map(self.map)
        else:
            super(AndroidMapView, self).child_added(child)

    def child_removed(self, child):
        if isinstance(child, AndroidMapItemBase):
            pass  #: It removes itself
        else:
            super(AndroidMapView, self).child_removed(child)

    def on_map_clicked(self, pos):
        """ Called when the map is clicked """
        d = self.declaration
        d.clicked({'click': 'short', 'position': tuple(pos)})

    def on_map_long_clicked(self, pos):
        """ Called when the map is clicked """
        d = self.declaration
        d.clicked({'click': 'long', 'position': tuple(pos)})

    # -------------------------------------------------------------------------
    # Camera API
    # -------------------------------------------------------------------------
    def on_camera_move_started(self, reason):
        d = self.declaration
        if reason == GoogleMap.CAMERA_REASON_GESTURE:
            d.dragging = True
        else:
            d.animating = True

    def on_camera_move_stopped(self):
        d = self.declaration
        d.dragging = False
        d.animating = False

    def on_camera_changed(self, camera):
        pos, zoom, tilt, bearing = camera
        d = self.declaration
        #: Don't update
        self._update_blocked = True
        try:
            d.camera_position = tuple(pos)
            d.camera_zoom = zoom
            d.camera_tilt = tilt
            d.camera_bearing = bearing
        finally:
            self._update_blocked = False

    # -------------------------------------------------------------------------
    # Marker API
    # -------------------------------------------------------------------------
    def on_marker_clicked(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            return m.on_click()
        return False

    def on_marker_drag(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            m.on_drag(pos)

    def on_marker_drag_start(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            m.on_drag_start(pos)

    def on_marker_drag_end(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            m.on_drag_end(pos)

    # -------------------------------------------------------------------------
    # Info window API
    # -------------------------------------------------------------------------
    def on_info_window_requested(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            return m.on_info_window_requested()

    def on_info_window_contents_requested(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            return m.on_info_window_contents_requested()

    def on_info_window_clicked(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            m.on_info_window_clicked('short')

    def on_info_window_long_clicked(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        if m:
            m.on_info_window_clicked('long')

    def on_info_window_closed(self, marker):
        mid, pos = marker
        m = self.markers.get(mid)
        #: This can come later when it's removed so check the declaration
        if m and m.declaration:
            m.on_info_window_closed()

    # -------------------------------------------------------------------------
    # Polygon and PolyLine API
    # -------------------------------------------------------------------------
    def on_poly_clicked(self, poly):
        m = self.markers.get(poly)
        if m:
            m.on_click()

    # -------------------------------------------------------------------------
    # Circle API
    # -------------------------------------------------------------------------
    def on_circle_clicked(self, circle):
        m = self.markers.get(circle)
        if m:
            m.on_click()

    # -------------------------------------------------------------------------
    # ProxyMapView API
    # -------------------------------------------------------------------------
    def set_map_bounds(self, bounds):
        raise NotImplementedError

    def set_map_type(self, map_type):
        if self.map:
            self.map.setMapType(GoogleMap.MAP_TYPES[map_type])
        else:
            self.options.mapType(GoogleMap.MAP_TYPES[map_type])

    def set_show_toolbar(self, show):
        if self.map:
            pass
        else:
            self.options.mapToolbarEnabled(show)

    def set_show_compass(self, show):
        if self.map:
            pass
        else:
            self.options.compassEnabled(show)

    def set_show_zoom_controls(self, show):
        if self.map:
            pass
        else:
            self.options.zoomControlsEnabled(show)

    def set_show_location(self, show):
        if self.map:
            if show:

                def on_result(allowed):
                    if allowed:
                        self.map.setMyLocationEnabled(True)
                    else:
                        self.declaration.show_location = False

                LocationManager.check_permission().then(on_result)
            else:
                self.map.setMyLocationEnabled(False)

    def set_show_buildings(self, show):
        if self.map:
            self.map.setBuildingsEnabled(show)

    def set_show_traffic(self, show):
        if self.map:
            self.map.setTrafficEnabled(show)

    def set_show_indoors(self, show):
        if self.map:
            self.map.setBuildingsEnabled(show)

    def update_camera(self):
        if self._update_blocked:
            return
        d = self.declaration
        if self.map:
            # Bit of a hack but it "should" work hahah
            # The future created to handle returned values creates an id for
            # itself. The bridge will save objects created (if they cannot be
            # packed by a specific Packer) using that ID, hence we can
            # reference it right away without actually waiting
            # until we get a return value back across the bridge.
            self.map.animateCamera(
                CameraUpdateFactory.newCameraPosition(
                    CameraPosition(LatLng(*d.camera_position), d.camera_zoom,
                                   d.camera_tilt, d.camera_bearing)))
        else:
            self.options.camera(
                CameraPosition(LatLng(*d.camera_position), d.camera_zoom,
                               d.camera_tilt, d.camera_bearing))

    def set_camera_zoom(self, zoom):
        self.update_camera()

    def set_camera_position(self, position):
        self.update_camera()

    def set_camera_bearing(self, bearing):
        self.update_camera()

    def set_camera_tilt(self, tilt):
        self.update_camera()

    def set_ambient_mode(self, enabled):
        if self.map:
            pass
        else:
            self.options.ambientEnabled(enabled)

    def set_lite_mode(self, enabled):
        if self.map:
            pass
        else:
            self.options.liteMode(enabled)

    def set_min_zoom(self, zoom):
        if self.map:
            self.map.setMinZoomPreference(zoom)
        else:
            self.options.minZoomPreference(zoom)

    def set_max_zoom(self, zoom):
        if self.map:
            self.map.setMaxZoomPreference(zoom)
        else:
            self.options.maxZoomPreference(zoom)

    def set_rotate_gestures(self, enabled):
        if self.map:
            pass
        else:
            self.options.rotateGesturesEnabled(enabled)

    def set_scroll_gestures(self, enabled):
        if self.map:
            pass
        else:
            self.options.scrollGesturesEnabled(enabled)

    def set_tilt_gestures(self, enabled):
        if self.map:
            pass
        else:
            self.options.tiltGesturesEnabled(enabled)

    def set_zoom_gestures(self, enabled):
        if self.map:
            pass
        else:
            self.options.zoomGesturesEnabled(enabled)
class AbsoluteOrientationTriggeredCalibrationController(
        LiveCalibrationController):

    is_ready = Bool(False)

    result_count = Int(128)
    errors_translation = Typed(np.ndarray)
    errors_rotation = Typed(np.ndarray)

    max_error_translation = Float(0.0)
    max_error_rotation = Float(0.0)

    initial_error_translation = Float(-1)
    initial_error_rotation = Float(-1)

    last_result = Value(None)

    results_txt = Value()
    progress_bar = Value()

    def setupController(self, active_widgets=None):
        super(AbsoluteOrientationTriggeredCalibrationController,
              self).setupController(active_widgets=active_widgets)
        if active_widgets is not None:
            w = active_widgets[0]
            self.results_txt = w.find('results_txt')
            self.progress_bar = w.find('progress_bar')

        if self.autocomplete_maxerror_str != "":
            translation, rotation = [
                s.strip() for s in self.autocomplete_maxerror_str.split(",")
            ]
            self.max_error_translation = float(translation)
            self.max_error_rotation = float(rotation)

        # needs to match the SRG !!
        self.sync_source = 'calib_absolute_orientation'
        self.required_sinks = [
            'calib_absolute_orientation',
        ]

        # setup a errors buffer
        self.errors_translation = np.array([np.nan] * self.result_count,
                                           dtype=np.double)
        self.errors_rotation = np.array([np.nan] * self.result_count,
                                        dtype=np.double)

        if self.facade is not None:
            self.facade.observe("is_loaded", self.connector_setup)

    def connector_setup(self, change):
        if change['value'] and self.verify_connector():
            self.connector.setup(self.facade.instance)
            self.connector.observe(self.sync_source, self.handle_data)
            self.is_ready = True

    def handle_data(self, c):
        if self.connector.calib_absolute_orientation is not None:
            ao = self.connector.calib_absolute_orientation.get()
            self.results_txt.text = "Result:\n%s" % str(ao)

            if self.last_result is not None:
                t_error = norm(ao.translation() -
                               self.last_result.translation())
                self.errors_translation[0] = t_error
                # implement simple ringbuffer
                self.errors_translation = np.roll(self.errors_translation, 1)

                if self.initial_error_translation == -1:
                    self.initial_error_translation = t_error

                r_error = abs(
                    math.Quaternion(ao.rotation().inverted() *
                                    self.last_result.rotation()).angle())
                self.errors_rotation[0] = r_error
                # implement simple ringbuffer
                self.errors_rotation = np.roll(self.errors_rotation, 1)

                if self.initial_error_rotation == -1:
                    self.initial_error_rotation = r_error

            self.last_result = ao

            # update progress bar
            if self.initial_error_translation != -1 and self.initial_error_translation != -1:
                t_p = t_error / (self.initial_error_translation -
                                 self.max_error_translation)
                r_p = r_error / (self.initial_error_rotation -
                                 self.max_error_rotation)
                pv = int(np.sqrt(1 - max(0, min(max(t_p, r_p), 1))) * 100)
                if pv > self.progress_bar.value:
                    self.progress_bar.value = pv

            # check if the minimum of self.result_count results have been received
            if not np.isnan(np.sum(self.errors_translation)) and not np.isnan(
                    np.sum(self.errors_rotation)):
                if np.all(self.errors_translation < self.max_error_translation) and \
                        np.all(self.errors_rotation < self.max_error_rotation):
                    log.info(
                        "Absolute Orientation: Results are satisfactory for translation (<%s) min: %s max: %s and rotation (<%s) min: %s max %s"
                        %
                        (self.max_error_translation,
                         np.min(self.errors_translation),
                         np.max(self.errors_translation),
                         self.max_error_rotation, np.min(self.errors_rotation),
                         np.max(self.errors_rotation)))
                    self.result_ok = True
                    self.progress_bar.value = 100
                    if self.autocomplete_enable:
                        self.stopCalibration()

    def handle_keypress(self, key):
        if not self.is_ready:
            return
        if key == 32:
            self.capturePoseAO()

    def capturePoseAO(self):
        if self.connector is not None:
            # use space a default trigger
            log.info("Capture Pose Absolute_orientation")
            self.connector.capture_pose(" ")
Ejemplo n.º 18
0
class TaskManagerPlugin(HasPrefPlugin):
    """Plugin responsible for collecting and providing tasks.

    """
    #: Folders containings templates which should be loaded.
    templates_folders = List(default=[TEMPLATE_PATH]).tag(pref=True)

    #: Known templates (store full path to .ini).
    #: This should not be manipulated by user code.
    templates = Dict()

    #: List of the filters.
    filters = List()

    #: Path to the file in which the names for the tasks are located.
    auto_task_path = Unicode(os.path.join(FOLDER_PATH,
                                          'tasknames.txt')).tag(pref=True)

    #: List of names to use when creating a new task.
    auto_task_names = List()

    def start(self):
        """Collect all declared tasks and start observers.

        """
        super(TaskManagerPlugin, self).start()
        core = self.workbench.get_plugin('enaml.workbench.core')
        core.invoke_command('ecpy.app.errors.enter_error_gathering')

        if not os.path.isdir(TEMPLATE_PATH):
            try:
                os.mkdir(TEMPLATE_PATH)
            except Exception:
                if TEMPLATE_PATH in self.templates_folders:
                    self.templates_folders.remove(TEMPLATE_PATH)
                core = self.workbench.get_plugin('enaml.workbench.core')
                msg = 'Failed to create template folder.'
                # Python 2 windows issue
                try:
                    msg += 'Traceback : %s' % format_exc()
                except UnicodeError:
                    msg += 'Failed to format error message.'
                core.invoke_command('ecpy.app.errors.signal',
                                    dict(kind='error', message=msg))

        self._filters = ExtensionsCollector(workbench=self.workbench,
                                            point=FILTERS_POINT,
                                            ext_class=TaskFilter)
        self._filters.start()
        self.filters = list(self._filters.contributions)

        self._configs = DeclaratorsCollector(workbench=self.workbench,
                                             point=CONFIG_POINT,
                                             ext_class=(TaskConfig,
                                                        TaskConfigs))

        self._configs.start()

        self._tasks = DeclaratorsCollector(workbench=self.workbench,
                                           point=TASK_EXT_POINT,
                                           ext_class=(Tasks, Task, Interfaces,
                                                      Interface))
        self._tasks.start()

        self._refresh_templates()
        if self.auto_task_path:
            self.load_auto_task_names()
        self._bind_observers()

        core.invoke_command('ecpy.app.errors.exit_error_gathering')

    def stop(self):
        """Discard collected tasks and remove observers.

        """
        self._unbind_observers()
        self._tasks.stop()
        self.templates.clear()
        self._filters.stop()
        self._configs.stop()

    def list_tasks(self, filter='All'):
        """List the known tasks using the specified filter.

        Parameters
        ----------
        filter : unicode, optional
            Name of the filter to use

        Returns
        -------
        tasks : list(unicode) or None
            Task ids selected by the filter, or None if the filter does not
            exist.

        """
        t_filter = self._filters.contributions.get(filter)
        if t_filter:
            return t_filter.filter_tasks(self._tasks.contributions,
                                         self.templates)

    def get_task_infos(self, task):
        """Access a given task infos.

        Parameters
        ----------
        task : unicode
            Id of the task class for which to return the actual class.

        Returns
        -------
        infos : TaskInfos or None
            Object containing all the infos about the requested task.
            This object should never be manipulated directly by user code.

        """
        if task not in self._tasks.contributions:
            return None

        return self._tasks.contributions[task]

    def get_task(self, task, view=False):
        """Access a given task class.

        Parameters
        ----------
        task : unicode
            Id of the task class for which to return the actual class.

        view : bool, optional
            Whether or not to return the view assoicated with the task.

        Returns
        -------
        task_cls : type or None
            Class associated to the requested task or None if the task was not
            found.

        task_view : EnamlDefMeta or None, optional
            Associated view if requested.

        """
        infos = self.get_task_infos(task)
        if infos is None:
            answer = None if not view else (None, None)
            return answer

        return infos.cls if not view else (infos.cls, infos.view)

    def get_tasks(self, tasks):
        """Access an ensemble of task classes.

        Parameters
        ----------
        tasks : list(unicode)
            Ids of the task classes for which to return the actual classes.

        Returns
        -------
        tasks_cls : dict
            Dictionary mapping the requested tasks to the actual classes.

        missing : list
            List of classes that were not found.

        """
        tasks_cls = {}
        missing = []
        for t in tasks:
            res = self.get_task(t)
            if res:
                tasks_cls[t] = res
            else:
                missing.append(t)

        return tasks_cls, missing

    def get_interface_infos(self, interface):
        """Access a given interface infos.

        Parameters
        ----------
        interface : tuple[unicode|tuple|list]
            - Name of the task class for which to return the actual class.
            - Name of the task to which this interface is linked and names of
              the intermediate interfaces if any (going from the most general
              ones to the more specialised ones).

        views : bool, optional
            Whether or not to return the views assoicated with the interface.

        Returns
        -------
        infos : InterfaceInfos
            Object containing all the infos about the requested interface.
            this object should never be manipulated directly by user code.

        """
        lookup_dict = self._tasks.contributions
        interface_cls_name, interface_anchor = interface
        if not isinstance(interface_anchor, (list, tuple)):
            interface_anchor = [interface_anchor]

        try:
            for anchor in interface_anchor:
                lookup_dict = lookup_dict[anchor].interfaces
        except KeyError:
            logger = logging.getLogger(__name__)
            msg = 'Looking for {} (anchor {}) failed to found {}'
            logger.debug(
                msg.format(interface_cls_name, interface_anchor, anchor))
            return None

        if interface_cls_name in lookup_dict:
            return lookup_dict[interface_cls_name]
        else:
            return None

    def get_interface(self, interface, views=False):
        """Access a given interface class.

        Parameters
        ----------
        interface: tuple[unicode|tuple|list]
            - Name of the task class for which to return the actual class.
            - Name of the task to which this interface is linked and names of
              the intermediate interfaces if any (going from the most general
              ones to the more specialised ones).

        views : bool, optional
            Whether or not to return the views assoicated with the interface.

        Returns
        -------
        interface_cls : type or None
            Class corresponding to the requested interface or None if the class
            was not found.

        views : list or None, optional
            List of views associated with the interface.

        """
        infos = self.get_interface_infos(interface)
        if infos is not None:
            return infos.cls if not views else (infos.cls, infos.views)
        else:
            return None if not views else (None, None)

    def get_interfaces(self, interfaces):
        """Access an ensemble of interface classes.

        Parameters
        ----------
        interfaces : list[tuple[unicode|tuple|list]]
            List of pairs (name of the interface class, corrisponding anchor)
            for which to return the actual classes.

        Returns
        -------
        interfaces_cls : dict
            Dictionary mapping the requested interfaces to the actual classes.

        missing : list
            List of classes that were not found.

        """
        interfaces_cls = {}
        missing = []
        for i in interfaces:
            i_cls = self.get_interface(i)
            if i_cls:
                interfaces_cls[i] = i_cls
            else:
                missing.append(i)

        return interfaces_cls, missing

    def get_config(self, task_id):
        """Access the proper config for a task.

        Parameters
        ----------
        task : unicode
           Id of the task for which a config is required

        Returns
        -------
        config : tuple
            Tuple containing the requested config object, and its
            visualisation.

        """
        templates = self.templates
        if task_id in templates:
            infos = configs = self._configs.contributions['__template__']
            config = infos.cls(manager=self, template_path=templates[task_id])
            return config, infos.view(config=config)

        elif task_id in self._tasks.contributions:
            configs = self._configs.contributions
            # Look up the hierarchy of the selected task to get the appropriate
            # TaskConfig
            task_class = self._tasks.contributions[task_id].cls
            for t_class in type.mro(task_class):
                if t_class in configs:
                    infos = configs[t_class]
                    c = infos.cls(manager=self, task_class=task_class)
                    return c, infos.view(config=c)

        return None, None

    def load_auto_task_names(self):
        """ Generate a list of task names from a file.

        """
        path = self.auto_task_path
        if not os.path.isfile(path):
            core = self.workbench.get_plugin('enaml.workbench.core')
            msg = 'Path {} does not point to a real file.'.format(path)
            core.invoke_command('ecpy.app.errors.signal',
                                dict(kind='error', message=msg))
            return

        with open(path) as f:
            aux = f.readlines()

        self.auto_task_names = [l.rstrip() for l in aux]

    # =========================================================================
    # --- Private API ---------------------------------------------------------
    # =========================================================================

    #: Dictionary storing all known tasks declarartion, using TaskInfos.
    _tasks = Typed(DeclaratorsCollector)

    #: Private storage keeping track of which extension declared which object.
    _extensions = Typed(defaultdict, (list, ))

    #: Contributed task filters.
    _filters = Typed(ExtensionsCollector)

    #: Contributed task configs.
    _configs = Typed(DeclaratorsCollector)

    #: Watchdog observer tracking changes to the templates folders.
    _observer = Typed(Observer, ())

    def _refresh_templates(self):
        """Refresh the list of template tasks.

        """
        # TODO rework to handle in an nicer fashion same template in multiple
        # folders
        templates = {}
        for path in self.templates_folders:
            if os.path.isdir(path):
                filenames = sorted(f for f in os.listdir(path)
                                   if f.endswith('.task.ini') and (
                                       os.path.isfile(os.path.join(path, f))))

                for filename in filenames:
                    template_path = os.path.join(path, filename)
                    # Beware redundant names are overwrited
                    name = filename[:-len('.task.ini')]
                    templates[name] = template_path
            else:
                logger = logging.getLogger(__name__)
                logger.warn('{} is not a valid directory'.format(path))

        self.templates = templates

    def _post_setattr_templates_folders(self, old, new):
        """Ensure that the template observer always watch the right folder.

        """
        self._observer.unschedule_all()

        for folder in self.templates_folders:
            if os.path.isdir(folder):
                handler = SystematicFileUpdater(self._update_templates)
                self._observer.schedule(handler, folder, recursive=True)

    def _update_templates(self):
        """Simply refresh the templates task.

        """
        self._refresh_templates()

    def _update_filters(self, change):
        """Update the available list of filters.

        """
        self.filters = list(change['value'].keys())

    def _bind_observers(self):
        """Setup all observers.

        """
        for folder in self.templates_folders:
            handler = SystematicFileUpdater(self._update_templates)
            self._observer.schedule(handler, folder, recursive=True)

        self._observer.start()

        self._filters.observe('contributions', self._update_filters)

    def _unbind_observers(self):
        """Remove all observers.

        """
        self._filters.unobserve('contributions', self._update_filters)

        self._observer.unschedule_all()
        self._observer.stop()
        try:
            self._observer.join()
        except RuntimeError:
            pass
Ejemplo n.º 19
0
class DockManager(Atom):
    """ A class which manages the docking behavior of a dock area.

    """
    #: The handler which holds the primary dock area.
    _dock_area = Typed(QDockArea)

    #: The overlay used when hovering over a dock area.
    _overlay = Typed(DockOverlay, ())

    #: The list of QDockFrame instances maintained by the manager. The
    #: QDockFrame class maintains this list in proper Z-order.
    _dock_frames = List()

    #: The set of QDockItem instances added to the manager.
    _dock_items = Typed(set, ())

    #: The distance to use for snapping floating dock frames.
    _snap_dist = Int(factory=lambda: QApplication.startDragDistance() * 2)

    #: A proximity handler which manages proximal floating frames.
    _proximity_handler = Typed(ProximityHandler, ())

    #: A container monitor which tracks toplevel container changes.
    _container_monitor = Typed(DockContainerMonitor)

    def _default__container_monitor(self):
        return DockContainerMonitor(self)

    def __init__(self, dock_area):
        """ Initialize a DockingManager.

        Parameters
        ----------
        dock_area : QDockArea
            The primary dock area to be managed. Docking will be
            restricted to this area and to windows spawned by the
            area.

        """
        assert dock_area is not None
        self._dock_area = dock_area
        self._overlay = DockOverlay(dock_area)

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------
    def dock_area(self):
        """ Get the dock area to which the manager is attached.

        Returns
        -------
        result : QDockArea
            The dock area to which the manager is attached.

        """
        return self._dock_area

    def add_item(self, item):
        """ Add a dock item to the dock manager.

        If the item has already been added, this is a no-op.

        Parameters
        ----------
        items : QDockItem
            The item to be managed by this dock manager. It will be
            reparented to a dock container and made available to the
            the layout system.

        """
        if item in self._dock_items:
            return
        self._dock_items.add(item)
        item._manager = self
        container = QDockContainer(self, self._dock_area)
        container.setDockItem(item)
        container.setObjectName(item.objectName())
        monitor = self._container_monitor
        container.topLevelChanged.connect(monitor.onTopLevelChanged)
        self._dock_frames.append(container)

    def remove_item(self, item):
        """ Remove a dock item from the dock manager.

        If the item has not been added to the manager, this is a no-op.

        Parameters
        ----------
        items : QDockItem
            The item to remove from the dock manager. It will be hidden
            and unparented, but not destroyed.

        """
        if item not in self._dock_items:
            return
        item._manager = None
        for container in self.dock_containers():
            if container.dockItem() is item:
                if not container.isWindow():
                    container.unplug()
                container.hide()
                self._free_container(container)
                break

    def save_layout(self):
        """ Get the current layout of the dock area.

        Returns
        -------
        result : docklayout
            A docklayout instance which represents the current layout
            state.

        """
        items = [self._dock_area] + self.floating_frames()
        return DockLayout(*map(LayoutSaver(), items))

    def apply_layout(self, layout):
        """ Apply a layout to the dock area.

        Parameters
        ----------
        layout : DockLayout
            The dock layout to apply to the managed area.

        """
        available = (i.objectName() for i in self._dock_items)
        DockLayoutValidator(available)(layout)
        LayoutBuilder(self)(layout)

    def update_layout(self, ops):
        """ Update the layout for a list of layout operations.

        Parameters
        ----------
        ops : list
            A list of LayoutOp objects to use for updating the layout.

        """
        builder = LayoutBuilder(self)
        for op in ops:
            builder(op)

    def destroy(self):
        """ Destroy the dock manager.

        This method will free all of the resources held by the dock
        manager. The primary dock area and dock items will not be
        destroyed. After the method is called, the dock manager is
        invalid and should no longer be used.

        """
        for frame in self._dock_frames:
            if isinstance(frame, QDockContainer):
                frame.setDockItem(None)
                frame.setParent(None, Qt.Widget)
                frame.hide()
        for frame in self._dock_frames:
            if isinstance(frame, QDockWindow):
                frame.setParent(None, Qt.Widget)
                frame.hide()
        for item in self._dock_items:
            item._manager = None
        self._dock_area.setCentralWidget(None)
        self._dock_area.setMaximizedWidget(None)
        del self._dock_area
        del self._dock_frames
        del self._dock_items
        del self._proximity_handler
        del self._container_monitor
        del self._overlay

    #--------------------------------------------------------------------------
    # Framework API
    #--------------------------------------------------------------------------
    def dock_containers(self):
        """ Get an iterable of QDockContainer instances.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Returns
        -------
        result : list
            A list of QDockContainer instances owned by this dock
            manager.

        """
        f = lambda w: isinstance(w, QDockContainer)
        return filter(f, self._dock_frames)

    def dock_windows(self):
        """ Get an iterable of QDockWindow instances.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Returns
        -------
        result : list
            A list of QDockWindow instances owned by this dock manager.

        """
        f = lambda w: isinstance(w, QDockWindow)
        return filter(f, self._dock_frames)

    def floating_frames(self):
        """ Get an iterable of floating dock frames.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Returns
        -------
        result : list
            A list toplevel QDockFrame instances.

        """
        f = lambda w: w.isWindow()
        return filter(f, self._dock_frames)

    def add_window(self, window):
        """ Add a floating QDockWindow to the dock manager.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Parameters
        ----------
        window : QDockWindow
            A newly created dock window which should be tracked by
            the dock manager.

        """
        self._dock_frames.append(window)
        self._proximity_handler.addFrame(window)

    def close_container(self, container, event):
        """ Handle a close request for a QDockContainer.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Parameters
        ----------
        window : QDockContainer
            The dock container to close.

        event : QCloseEvent
            The close event passed to the event handler.

        """
        item = container.dockItem()
        if item is None or item.close():
            if not container.isWindow():
                container.unplug()
            self._free_container(container)
        else:
            event.ignore()

    def close_window(self, window, event):
        """ Handle a close request for a QDockWindow.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Parameters
        ----------
        window : QDockWindow
            The dock window to close.

        event : QCloseEvent
            The close event passed to the event handler.

        """
        area = window.dockArea()
        if area is not None:
            containers = list(iter_containers(area))
            geometries = {}
            for container in containers:
                pos = container.mapToGlobal(QPoint(0, 0))
                size = container.size()
                geometries[container] = QRect(pos, size)
            for container, ignored in area.dockBarContainers():
                containers.append(container)
                size = container.sizeHint()
                geometries[container] = QRect(window.pos(), size)
            for container in containers:
                if not container.close():
                    container.unplug()
                    container.float()
                    container.setGeometry(geometries[container])
                    container.show()
        self._free_window(window)

    def raise_frame(self, frame):
        """ Raise a frame to the top of the Z-order.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Parameters
        ----------
        frame : QDockFrame
            The frame to raise to the top of the Z-order.

        """
        frames = self._dock_frames
        handler = self._proximity_handler
        if handler.hasLinkedFrames(frame):
            linked = set(handler.linkedFrames(frame))
            ordered = [f for f in frames if f in linked]
            for other in ordered:
                frames.remove(other)
                frames.append(other)
                other.raise_()
            frame.raise_()
        frames.remove(frame)
        frames.append(frame)

    def frame_resized(self, frame):
        """ Handle the post-processing for a resized floating frame.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code.

        Parameters
        ----------
        frame : QDockFrame
            The frame which has been resized.

        """
        # If the frame is linked, the resize may have changed the frame
        # geometry such that the existing links are no longer valid.
        # The links are refreshed and the link button state is updated.
        if frame.isLinked():
            handler = self._proximity_handler
            handler.updateLinks(frame)
            if not handler.hasLinkedFrames(frame):
                frame.setLinked(False)

    def drag_move_frame(self, frame, target_pos, mouse_pos):
        """ Move the floating frame to the target position.

        This method is called by a floating frame in response to a user
        moving it by dragging on it's title bar. It takes into account
        neighboring windows and will snap the frame edge to another
        window if it comes close to the boundary. It also ensures that
        the guide overlays are shown at the proper position. This method
        should not be called by user code.

        Parameters
        ----------
        frame : QDockFrame
            The floating QDockFrame which should be moved.

        target_pos : QPoint
            The global position which is the target of the move.

        mouse_pos : QPoint
            The global mouse position.

        """
        # If the frame is linked, it and any of its linked frames are
        # moved the same amount with no snapping. An unlinked window
        # is free to move and will snap to any other floating window
        # that has an opposite edge lying within the snap distance.
        # The overlay is hidden when the frame has proximal frames
        # since such a frame is not allowed to be docked.
        show_drag_overlay = True
        handler = self._proximity_handler
        if frame.isLinked():
            delta = target_pos - frame.pos()
            frame.move(target_pos)
            if handler.hasLinkedFrames(frame):
                show_drag_overlay = False
                for other in handler.linkedFrames(frame):
                    other.move(other.pos() + delta)
        else:
            f_size = frame.frameGeometry().size()
            f_rect = QRect(target_pos, f_size)
            f_x = target_pos.x()
            f_y = target_pos.y()
            f_w = f_size.width()
            f_h = f_size.height()
            dist = self._snap_dist
            filt = lambda n: -dist < n < dist
            for other in handler.proximalFrames(f_rect, dist):
                if other is not frame:
                    o_geo = other.frameGeometry()
                    o_x = o_geo.left()
                    o_y = o_geo.top()
                    o_right = o_x + o_geo.width()
                    o_bottom = o_y + o_geo.height()
                    dx = filter(filt, (
                        o_x - f_x,
                        o_x - (f_x + f_w),
                        o_right - f_x,
                        o_right - (f_x + f_w),
                    ))
                    if dx:
                        f_x += min(dx)
                    dy = filter(filt, (
                        o_y - f_y,
                        o_y - (f_y + f_h),
                        o_bottom - f_y,
                        o_bottom - (f_y + f_h),
                    ))
                    if dy:
                        f_y += min(dy)
            frame.move(f_x, f_y)
        if show_drag_overlay:
            self._update_drag_overlay(frame, mouse_pos)
        else:
            self._overlay.hide()

    def drag_release_frame(self, frame, pos):
        """ Handle the dock frame being released by the user.

        This method is called by the framework at the appropriate times
        and should not be called directly by user code. It will redock
        a floating dock item if it is released over a dock guide.

        Parameters
        ----------
        frame : QDockFrame
            The dock frame being dragged by the user.

        pos : QPoint
            The global coordinates of the mouse position.

        """
        # Docking is disallowed for frames which have linked proximal
        # frames, or if the target dock area has a maximized widget.
        # This prevents a situation where the docking logic would be
        # non-sensical and maintains a consistent user experience.
        overlay = self._overlay
        overlay.hide()
        guide = overlay.guide_at(pos)
        if guide == QGuideRose.Guide.NoGuide:
            return
        if self._proximity_handler.hasLinkedFrames(frame):
            return
        builder = LayoutBuilder(self)
        target = self._dock_target(frame, pos)
        if isinstance(target, QDockArea):
            if target.maximizedWidget() is not None:
                return
            with builder.drop_frame(frame):
                local = target.mapFromGlobal(pos)
                widget = layout_hit_test(target, local)
                plug_frame(target, widget, frame, guide)
        elif isinstance(target, QDockContainer):
            with builder.dock_context(target):
                with builder.drop_frame(frame):
                    area = target.parentDockArea()
                    if area is not None:
                        plug_frame(area, target, frame, guide)

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------
    def _free_container(self, container):
        """ Free the resources attached to the container.

        Parameters
        ----------
        container : QDockContainer
            The container which should be cleaned up. It should be
            unplugged from any layout before being passed to this
            method.

        """
        item = container.dockItem()
        container.setParent(None)
        container.setDockItem(None)
        container._manager = None
        self._dock_items.discard(item)
        self._dock_frames.remove(container)
        self._proximity_handler.removeFrame(container)

    def _free_window(self, window):
        """ Free the resources attached to the window.

        Parameters
        ----------
        window : QDockWindow
            The Window which should be cleaned up.

        """
        window.setParent(None)
        window.setDockArea(None)
        window._manager = None
        self._dock_frames.remove(window)
        self._proximity_handler.removeFrame(window)

    def _iter_dock_targets(self, frame):
        """ Get an iterable of potential dock targets.

        Parameters
        ----------
        frame : QDockFrame
            The frame which is being docked, and therefore excluded
            from the target search.

        Returns
        -------
        result : generator
            A generator which yields the dock container and dock area
            instances which are potential dock targets.

        """
        for target in reversed(self._dock_frames):
            if target is not frame and target.isWindow():
                if isinstance(target, QDockContainer):
                    yield target
                elif isinstance(target, QDockWindow):
                    yield target.dockArea()
        yield self._dock_area

    def _dock_target(self, frame, pos):
        """ Get the dock target for the given frame and position.

        Parameters
        ----------
        frame : QDockFrame
            The dock frame which should be docked.

        pos : QPoint
            The global mouse position.

        Returns
        -------
        result : QDockArea, QDockContainer, or None
            The potential dock target for the frame and position.

        """
        for target in self._iter_dock_targets(frame):
            # Hit test the central pane instead of the entire dock area
            # so that mouse movement over the dock bars is ignored.
            if isinstance(target, QDockArea):
                pane = target.centralPane()
                local = pane.mapFromGlobal(pos)
                if pane.rect().contains(local):
                    return target
            else:
                local = target.mapFromGlobal(pos)
                if target.rect().contains(local):
                    return target

    def _update_drag_overlay(self, frame, pos):
        """ Update the overlay for a dragged frame.

        Parameters
        ----------
        frame : QDockFrame
            The dock frame being dragged by the user.

        pos : QPoint
            The global coordinates of the mouse position.

        """
        overlay = self._overlay
        target = self._dock_target(frame, pos)
        if isinstance(target, QDockContainer):
            local = target.mapFromGlobal(pos)
            overlay.mouse_over_widget(target, local)
        elif isinstance(target, QDockArea):
            # Disallow docking onto an area with a maximized widget.
            # This prevents a non-intuitive user experience.
            if target.maximizedWidget() is not None:
                overlay.hide()
                return
            local = target.mapFromGlobal(pos)
            widget = layout_hit_test(target, local)
            overlay.mouse_over_area(target, widget, local)
        else:
            overlay.hide()
Ejemplo n.º 20
0
class QtWidget(QtToolkitObject, ProxyWidget):
    """ A Qt implementation of an Enaml ProxyWidget.

    """
    #: A reference to the toolkit widget created by the proxy.
    widget = Typed(QWidget)

    #: A QWidgetItem created on-demand for the widget. This is used by
    #: the layout engine to compute correct size hints for the widget.
    widget_item = Typed(QWidgetItem)

    def _default_widget_item(self):
        return QWidgetItem(self.widget)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying QWidget object.

        """
        self.widget = QWidget(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying QWidget object.

        """
        super(QtWidget, self).init_widget()
        d = self.declaration
        if d.background:
            self.set_background(d.background)
        if d.foreground:
            self.set_foreground(d.foreground)
        if d.font:
            self.set_font(d.font)
        if d.show_focus_rect is not None:
            self.set_show_focus_rect(d.show_focus_rect)
        if -1 not in d.minimum_size:
            self.set_minimum_size(d.minimum_size)
        if -1 not in d.maximum_size:
            self.set_maximum_size(d.maximum_size)
        if d.tool_tip:
            self.set_tool_tip(d.tool_tip)
        if d.status_tip:
            self.set_status_tip(d.status_tip)
        self.set_enabled(d.enabled)
        # Don't make toplevel widgets visible during init or they will
        # flicker onto the screen. This applies particularly for things
        # like status bar widgets which are created with no parent and
        # then reparented by the status bar. Real top-level widgets must
        # be explicitly shown by calling their .show() method after they
        # are created.
        if self.widget.parent() or not d.visible:
            self.set_visible(d.visible)

    #--------------------------------------------------------------------------
    # ProxyWidget API
    #--------------------------------------------------------------------------
    def set_minimum_size(self, min_size):
        """ Sets the minimum size of the widget.

        """
        # QWidget uses (0, 0) as the minimum size.
        if -1 in min_size:
            min_size = (0, 0)
        self.widget.setMinimumSize(QSize(*min_size))

    def set_maximum_size(self, max_size):
        """ Sets the maximum size of the widget.

        """
        # QWidget uses 16777215 as the max size
        if -1 in max_size:
            max_size = (16777215, 16777215)
        self.widget.setMaximumSize(QSize(*max_size))

    def set_enabled(self, enabled):
        """ Set the enabled state of the widget.

        """
        self.widget.setEnabled(enabled)

    def set_visible(self, visible):
        """ Set the visibility of the widget.

        """
        self.widget.setVisible(visible)

    def set_background(self, background):
        """ Set the background color of the widget.

        """
        widget = self.widget
        role = widget.backgroundRole()
        if background is not None:
            qcolor = get_cached_qcolor(background)
            widget.setAutoFillBackground(True)
        else:
            app_palette = QApplication.instance().palette(widget)
            qcolor = app_palette.color(role)
            widget.setAutoFillBackground(False)
        palette = widget.palette()
        palette.setColor(role, qcolor)
        widget.setPalette(palette)

    def set_foreground(self, foreground):
        """ Set the foreground color of the widget.

        """
        widget = self.widget
        role = widget.foregroundRole()
        if foreground is not None:
            qcolor = get_cached_qcolor(foreground)
        else:
            app_palette = QApplication.instance().palette(widget)
            qcolor = app_palette.color(role)
        palette = widget.palette()
        palette.setColor(role, qcolor)
        widget.setPalette(palette)

    def set_font(self, font):
        """ Set the font of the widget.

        """
        widget = self.widget
        if font is not None:
            widget.setFont(get_cached_qfont(font))
        else:
            widget.setFont(QFont())

    def set_show_focus_rect(self, show):
        """ Set whether or not to show the focus rect.

        This is currently only supported on OSX.

        """
        if sys.platform == 'darwin':
            self.widget.setAttribute(Qt.WA_MacShowFocusRect, bool(show))

    def set_tool_tip(self, tool_tip):
        """ Set the tool tip for the widget.

        """
        self.widget.setToolTip(tool_tip)

    def set_status_tip(self, status_tip):
        """ Set the status tip for the widget.

        """
        self.widget.setStatusTip(status_tip)

    def ensure_visible(self):
        """ Ensure the widget is visible.

        """
        self.widget.setVisible(True)

    def ensure_hidden(self):
        """ Ensure the widget is hidden.

        """
        self.widget.setVisible(False)
Ejemplo n.º 21
0
class QtWidget(QtToolkitObject, ProxyWidget):
    """ A Qt implementation of an Enaml ProxyWidget.

    """
    #: A reference to the toolkit widget created by the proxy.
    widget = Typed(QWidget)

    #: A private copy of the declaration features. This ensures that
    #: feature cleanup will proceed correctly in the event that user
    #: code modifies the declaration features value at runtime.
    _features = Coerced(Feature.Flags)

    #: Internal storage for the shared widget action.
    _widget_action = Typed(QWidgetAction)

    #: Internal storage for the drag origin position.
    _drag_origin = Typed(QPoint)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying QWidget object.

        """
        self.widget = QWidget(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying QWidget object.

        """
        super(QtWidget, self).init_widget()
        widget = self.widget
        focus_registry.register(widget, self)
        self._setup_features()
        d = self.declaration
        if d.background:
            self.set_background(d.background)
        if d.foreground:
            self.set_foreground(d.foreground)
        if d.font:
            self.set_font(d.font)
        if -1 not in d.minimum_size:
            self.set_minimum_size(d.minimum_size)
        if -1 not in d.maximum_size:
            self.set_maximum_size(d.maximum_size)
        if d.tool_tip:
            self.set_tool_tip(d.tool_tip)
        if d.status_tip:
            self.set_status_tip(d.status_tip)
        if not d.enabled:
            self.set_enabled(d.enabled)
        self.refresh_style_sheet()
        # Don't make toplevel widgets visible during init or they will
        # flicker onto the screen. This applies particularly for things
        # like status bar widgets which are created with no parent and
        # then reparented by the status bar. Real top-level widgets must
        # be explicitly shown by calling their .show() method after they
        # are created.
        if widget.parent() or not d.visible:
            self.set_visible(d.visible)

    def destroy(self):
        """ Destroy the underlying QWidget object.

        """
        self._teardown_features()
        focus_registry.unregister(self.widget)
        super(QtWidget, self).destroy()
        # If a QWidgetAction was created for this widget, then it has
        # taken ownership of the widget and the widget will be deleted
        # when the QWidgetAction is garbage collected. This means the
        # superclass destroy() method must run before the reference to
        # the QWidgetAction is dropped.
        del self._widget_action

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------
    def _setup_features(self):
        """ Setup the advanced widget feature handlers.

        """
        features = self._features = self.declaration.features
        if not features:
            return
        if features & Feature.FocusTraversal:
            self.hook_focus_traversal()
        if features & Feature.FocusEvents:
            self.hook_focus_events()
        if features & Feature.DragEnabled:
            self.hook_drag()
        if features & Feature.DropEnabled:
            self.hook_drop()

    def _teardown_features(self):
        """ Teardowns the advanced widget feature handlers.

        """
        features = self._features
        if not features:
            return
        if features & Feature.FocusTraversal:
            self.unhook_focus_traversal()
        if features & Feature.FocusEvents:
            self.unhook_focus_events()
        if features & Feature.DragEnabled:
            self.unhook_drag()
        if features & Feature.DropEnabled:
            self.unhook_drop()

    #--------------------------------------------------------------------------
    # Protected API
    #--------------------------------------------------------------------------
    def refresh_style_sheet(self):
        """ Refresh the widget style sheet with the current style data.

        """
        parts = []
        name = self.widget.objectName()
        for style in StyleCache.styles(self.declaration):
            t = translate_style(name, style)
            if t:
                parts.append(t)
        if len(parts) > 0:
            stylesheet = u'\n\n'.join(parts)
        else:
            stylesheet = u''
        self.widget.setStyleSheet(stylesheet)

    def tab_focus_request(self, reason):
        """ Handle a custom tab focus request.

        This method is called when focus is being set on the proxy
        as a result of a user-implemented focus traversal handler.
        This can be reimplemented by subclasses as needed.

        Parameters
        ----------
        reason : Qt.FocusReason
            The reason value for the focus request.

        Returns
        -------
        result : bool
            True if focus was set, False otherwise.

        """
        widget = self.focus_target()
        if not widget.focusPolicy & Qt.TabFocus:
            return False
        if not widget.isEnabled():
            return False
        if not widget.isVisibleTo(widget.window()):
            return False
        widget.setFocus(reason)
        return False

    def focus_target(self):
        """ Return the current focus target for a focus request.

        This can be reimplemented by subclasses as needed. The default
        implementation of this method returns the current proxy widget.

        """
        return self.widget

    def hook_focus_traversal(self):
        """ Install the hooks for focus traversal.

        This method may be overridden by subclasses as needed.

        """
        self.widget.focusNextPrevChild = self.focusNextPrevChild

    def unhook_focus_traversal(self):
        """ Remove the hooks for the next/prev child focusing.

        This method may be overridden by subclasses as needed.

        """
        del self.widget.focusNextPrevChild

    def hook_focus_events(self):
        """ Install the hooks for focus events.

        This method may be overridden by subclasses as needed.

        """
        widget = self.widget
        widget.focusInEvent = self.focusInEvent
        widget.focusOutEvent = self.focusOutEvent

    def unhook_focus_events(self):
        """ Remove the hooks for the focus events.

        This method may be overridden by subclasses as needed.

        """
        widget = self.widget
        del widget.focusInEvent
        del widget.focusOutEvent

    def focusNextPrevChild(self, next_child):
        """ The default 'focusNextPrevChild' implementation.

        """
        fd = focus_registry.focused_declaration()
        if next_child:
            child = self.declaration.next_focus_child(fd)
            reason = Qt.TabFocusReason
        else:
            child = self.declaration.previous_focus_child(fd)
            reason = Qt.BacktabFocusReason
        if child is not None and child.proxy_is_active:
            return child.proxy.tab_focus_request(reason)
        widget = self.widget
        return type(widget).focusNextPrevChild(widget, next_child)

    def focusInEvent(self, event):
        """ The default 'focusInEvent' implementation.

        """
        widget = self.widget
        type(widget).focusInEvent(widget, event)
        self.declaration.focus_gained()

    def focusOutEvent(self, event):
        """ The default 'focusOutEvent' implementation.

        """
        widget = self.widget
        type(widget).focusOutEvent(widget, event)
        self.declaration.focus_lost()

    def hook_drag(self):
        """ Install the hooks for drag operations.

        """
        widget = self.widget
        widget.mousePressEvent = self.mousePressEvent
        widget.mouseMoveEvent = self.mouseMoveEvent
        widget.mouseReleaseEvent = self.mouseReleaseEvent

    def unhook_drag(self):
        """ Remove the hooks for drag operations.

        """
        widget = self.widget
        del widget.mousePressEvent
        del widget.mouseMoveEvent
        del widget.mouseReleaseEvent

    def mousePressEvent(self, event):
        """ Handle the mouse press event for a drag operation.

        """
        if event.button() == Qt.LeftButton:
            self._drag_origin = event.pos()
        widget = self.widget
        type(widget).mousePressEvent(widget, event)

    def mouseMoveEvent(self, event):
        """ Handle the mouse move event for a drag operation.

        """
        if event.buttons() & Qt.LeftButton and self._drag_origin is not None:
            dist = (event.pos() - self._drag_origin).manhattanLength()
            if dist >= QApplication.startDragDistance():
                self.do_drag()
                self._drag_origin = None
                return
        widget = self.widget
        type(widget).mouseMoveEvent(widget, event)

    def mouseReleaseEvent(self, event):
        """ Handle the mouse release event for the drag operation.

        """
        if event.button() == Qt.LeftButton:
            self._drag_origin = None
        widget = self.widget
        type(widget).mouseReleaseEvent(widget, event)

    def hook_drop(self):
        """ Install hooks for drop operations.

        """
        widget = self.widget
        widget.setAcceptDrops(True)
        widget.dragEnterEvent = self.dragEnterEvent
        widget.dragMoveEvent = self.dragMoveEvent
        widget.dragLeaveEvent = self.dragLeaveEvent
        widget.dropEvent = self.dropEvent

    def unhook_drop(self):
        """ Remove hooks for drop operations.

        """
        widget = self.widget
        widget.setAcceptDrops(False)
        del widget.dragEnterEvent
        del widget.dragMoveEvent
        del widget.dragLeaveEvent
        del widget.dropEvent

    def do_drag(self):
        """ Perform the drag operation for the widget.

        """
        drag_data = self.declaration.drag_start()
        if drag_data is None:
            return
        widget = self.widget
        qdrag = QDrag(widget)
        qdrag.setMimeData(drag_data.mime_data.q_data())
        if drag_data.image is not None:
            qimg = get_cached_qimage(drag_data.image)
            qdrag.setPixmap(QPixmap.fromImage(qimg))
        else:
            if __version_info__ < (5, ):
                qdrag.setPixmap(QPixmap.grabWidget(widget))
            else:
                qdrag.setPixmap(widget.grab())
        if drag_data.hotspot:
            qdrag.setHotSpot(QPoint(*drag_data.hotspot))
        else:
            cursor_position = widget.mapFromGlobal(QCursor.pos())
            qdrag.setHotSpot(cursor_position)
        default = Qt.DropAction(drag_data.default_drop_action)
        supported = Qt.DropActions(drag_data.supported_actions)
        qresult = qdrag.exec_(supported, default)
        self.declaration.drag_end(drag_data, DropAction(int(qresult)))

    def dragEnterEvent(self, event):
        """ Handle the drag enter event for the widget.

        """
        self.declaration.drag_enter(QtDropEvent(event))

    def dragMoveEvent(self, event):
        """ Handle the drag move event for the widget.

        """
        self.declaration.drag_move(QtDropEvent(event))

    def dragLeaveEvent(self, event):
        """ Handle the drag leave event for the widget.

        """
        self.declaration.drag_leave()

    def dropEvent(self, event):
        """ Handle the drop event for the widget.

        """
        self.declaration.drop(QtDropEvent(event))

    #--------------------------------------------------------------------------
    # Framework API
    #--------------------------------------------------------------------------
    def get_action(self, create=False):
        """ Get the shared widget action for this widget.

        This API is used to support widgets in tool bars and menus.

        Parameters
        ----------
        create : bool, optional
            Whether to create the action if it doesn't already exist.
            The default is False.

        Returns
        -------
        result : QWidgetAction or None
            The cached widget action or None, depending on arguments.

        """
        action = self._widget_action
        if action is None and create:
            action = self._widget_action = QWidgetAction(None)
            action.setDefaultWidget(self.widget)
        return action

    #--------------------------------------------------------------------------
    # ProxyWidget API
    #--------------------------------------------------------------------------
    def set_minimum_size(self, min_size):
        """ Sets the minimum size of the widget.

        """
        # QWidget uses (0, 0) as the minimum size.
        if -1 in min_size:
            min_size = (0, 0)
        self.widget.setMinimumSize(QSize(*min_size))

    def set_maximum_size(self, max_size):
        """ Sets the maximum size of the widget.

        """
        # QWidget uses 16777215 as the max size
        if -1 in max_size:
            max_size = (16777215, 16777215)
        self.widget.setMaximumSize(QSize(*max_size))

    def set_enabled(self, enabled):
        """ Set the enabled state of the widget.

        """
        self.widget.setEnabled(enabled)
        action = self._widget_action
        if action is not None:
            action.setEnabled(enabled)

    def set_visible(self, visible):
        """ Set the visibility of the widget.

        """
        self.widget.setVisible(visible)
        action = self._widget_action
        if action is not None:
            action.setVisible(visible)

    def set_background(self, background):
        """ Set the background color of the widget.

        """
        widget = self.widget
        role = widget.backgroundRole()
        if background is not None:
            qcolor = get_cached_qcolor(background)
            widget.setAutoFillBackground(True)
        else:
            app_palette = QApplication.instance().palette(widget)
            qcolor = app_palette.color(role)
            widget.setAutoFillBackground(False)
        palette = widget.palette()
        palette.setColor(role, qcolor)
        widget.setPalette(palette)

    def set_foreground(self, foreground):
        """ Set the foreground color of the widget.

        """
        widget = self.widget
        role = widget.foregroundRole()
        if foreground is not None:
            qcolor = get_cached_qcolor(foreground)
        else:
            app_palette = QApplication.instance().palette(widget)
            qcolor = app_palette.color(role)
        palette = widget.palette()
        palette.setColor(role, qcolor)
        widget.setPalette(palette)

    def set_font(self, font):
        """ Set the font of the widget.

        """
        if font is not None:
            self.widget.setFont(get_cached_qfont(font))
        else:
            self.widget.setFont(QFont())

    def set_tool_tip(self, tool_tip):
        """ Set the tool tip for the widget.

        """
        self.widget.setToolTip(tool_tip)

    def set_status_tip(self, status_tip):
        """ Set the status tip for the widget.

        """
        self.widget.setStatusTip(status_tip)

    def ensure_visible(self):
        """ Ensure the widget is visible.

        """
        self.widget.setVisible(True)
        action = self._widget_action
        if action is not None:
            action.setVisible(True)

    def ensure_hidden(self):
        """ Ensure the widget is hidden.

        """
        self.widget.setVisible(False)
        action = self._widget_action
        if action is not None:
            action.setVisible(False)

    def restyle(self):
        """ Restyle the widget with the current style data.

        """
        self.refresh_style_sheet()

    def set_focus(self):
        """ Set the keyboard input focus to this widget.

        """
        self.focus_target().setFocus(Qt.OtherFocusReason)

    def clear_focus(self):
        """ Clear the keyboard input focus from this widget.

        """
        self.focus_target().clearFocus()

    def has_focus(self):
        """ Test whether this widget has input focus.

        """
        return self.focus_target().hasFocus()

    def focus_next_child(self):
        """ Give focus to the next widget in the focus chain.

        """
        self.focus_target().focusNextChild()

    def focus_previous_child(self):
        """ Give focus to the previous widget in the focus chain.

        """
        self.focus_target().focusPreviousChild()
Ejemplo n.º 22
0
class Channel(PSIContribution):

    #: Globally-unique name of channel used for identification
    name = d_(Str()).tag(metadata=True)

    #: Code assigned by subclasses to identify channel type
    type_code = Str()

    #: Unique reference label used for tracking identity throughout
    #: psiexperiment
    reference = Str().tag(metadata=True)

    #: Label of channel used in GUI
    label = d_(Str()).tag(metadata=True)

    #: Is channel active during experiment?
    active = Property().tag(metadata=True)

    # SI unit (e.g., V)
    unit = d_(Str()).tag(metadata=True)

    # Number of samples to acquire before task ends. Typically will be set to
    # 0 to indicate continuous acquisition.
    samples = d_(Int(0)).tag(metadata=True)

    # Used to properly configure data storage.
    dtype = d_(Str()).tag(metadata=True)

    # Parent engine (automatically derived by Enaml hierarchy)
    engine = Property().tag(metadata=True)

    # Calibration of channel
    calibration = d_(Typed(BaseCalibration, factory=FlatCalibration.unity))
    calibration.tag(metadata=True)

    # Can the user modify the channel calibration?
    calibration_user_editable = d_(Bool(False)).tag(metadata=True)

    filter_delay = d_(Float(0).tag(metadata=True))

    def _observe_name(self, event):
        self.reference = self._default_reference()

    def _default_reference(self):
        return f'{self.type_code}::{self.name}'

    def __init__(self, *args, **kwargs):
        # This is a hack due to the fact that name is defined as a Declarative
        # member and each Mixin will overwrite whether or not the name is
        # tagged.
        super().__init__(*args, **kwargs)
        self.members()['name'].tag(metadata=True)

    def _get_engine(self):
        return self.parent

    def _set_engine(self, engine):
        self.set_parent(engine)

    def configure(self):
        pass

    def sync_start(self, channel):
        '''
        Synchronize with channel so that sampling begins at the same time

        Parameters
        ----------
        channel : instance of Channel
            Channel to synchronize with.
        '''

        raise NotImplementedError

    def _get_active(self):
        raise NotImplementedError

    def __str__(self):
        return self.label
Ejemplo n.º 23
0
class DockOverlay(Atom):
    """ An object which manages the overlays for dock widgets.

    This manager handles the state transitions for the overlays. The
    transitions are performed on a slightly-delayed timer to provide
    a more fluid user interaction experience.

    """
    # PySide requires weakrefs for using bound methods as slots
    if QT_API == 'pyside':
        __slots__ = '__weakref__'

    #: The size of the rubber band when docking on the border, in px.
    border_size = Int(60)

    #: The delay to use when triggering the rose timer, in ms.
    rose_delay = Int(30)

    #: The delay to use when triggering the band timer, in ms.
    band_delay = Int(50)

    #: The target opacity to use when making the band visible.
    band_target_opacity = Float(1.0)

    #: The duration of the band visibilty animation, in ms.
    band_vis_duration = Int(100)

    #: the duration of the band geometry animation, in ms.
    band_geo_duration = Int(100)

    #: The overlayed guide rose.
    _rose = Typed(QGuideRose, ())

    #: The overlayed rubber band.
    _band = Typed(QDockRubberBand, ())

    #: The property animator for the rubber band geometry.
    _geo_animator = Typed(QPropertyAnimation)

    #: The property animator for the rubber band visibility.
    _vis_animator = Typed(QPropertyAnimation)

    #: The target mode to apply to the rose on timeout.
    _target_rose_mode = Int(QGuideRose.Mode.NoMode)

    #: The target geometry to apply to rubber band on timeout.
    _target_band_geo = Typed(QRect, factory=lambda: QRect())

    #: The value of the last guide which was hit in the rose.
    _last_guide = Int(-1)

    #: A flag indicating whether it is safe to show the band.
    _show_band = Bool(False)

    #: The hover position of the mouse to use for state changes.
    _hover_pos = Typed(QPoint, factory=lambda: QPoint())

    #: The timer for changing the state of the rose.
    _rose_timer = Typed(QTimer)

    #: The timer for changing the state of the band.
    _band_timer = Typed(QTimer)

    def __init__(self, parent=None):
        """ Initialize a DockOverlay.

        Parameters
        ----------
        parent : QWidget, optional
            The parent of the overlay. This will be used as the parent
            widget for the dock rubber band. The overlay guides do not
            have a parent.

        """
        self._band = QDockRubberBand(parent)

    #--------------------------------------------------------------------------
    # Default Value Methods
    #--------------------------------------------------------------------------
    def _default__rose_timer(self):
        """ Create the default timer for the rose state changes.

        """
        timer = QTimer()
        timer.setSingleShot(True)
        timer.timeout.connect(self._on_rose_timer)
        return timer

    def _default__band_timer(self):
        """ Create the default timer for the band state changes.

        """
        timer = QTimer()
        timer.setSingleShot(True)
        timer.timeout.connect(self._on_band_timer)
        return timer

    def _default__geo_animator(self):
        """ Create the default property animator for the rubber band.

        """
        p = QPropertyAnimation(self._band, 'geometry')
        p.setDuration(self.band_geo_duration)
        return p

    def _default__vis_animator(self):
        """ Create the default property animator for the rubber band.

        """
        p = QPropertyAnimation(self._band, 'windowOpacity')
        p.setDuration(self.band_vis_duration)
        p.finished.connect(self._on_vis_finished)
        return p

    #--------------------------------------------------------------------------
    # Timer Handlers
    #--------------------------------------------------------------------------
    def _on_rose_timer(self):
        """ Handle the timeout event for the internal rose timer.

        This handler transitions the rose to its new state and updates
        the position of the rubber band.

        """
        rose = self._rose
        rose.setMode(self._target_rose_mode)
        rose.mouseOver(self._hover_pos)
        self._show_band = True
        self._update_band_state()

    def _on_band_timer(self):
        """ Handle the timeout event for the internal band timer.

        This handler updates the position of the rubber band.

        """
        self._update_band_state()

    #--------------------------------------------------------------------------
    # Animation Handlers
    #--------------------------------------------------------------------------
    def _on_vis_finished(self):
        """ Handle the 'finished' signal from the visibility animator.

        This handle will hide the rubber band when its opacity is 0.

        """
        band = self._band
        if band.windowOpacity() == 0.0:
            band.hide()

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------
    def _update_band_state(self):
        """ Refresh the geometry and visible state of the rubber band.

        The state will be updated using animated properties to provide
        a nice fluid user experience.

        """
        # A valid geometry indicates that the rubber should be shown on
        # the screen. An invalid geometry means it should be hidden. If
        # the validity is changed during animation, the animators are
        # restarted using the current state as their starting point.
        band = self._band
        geo = self._target_band_geo
        if geo.isValid() and self._show_band:
            # If the band is already hidden, the geometry animation can
            # be bypassed since the band can be located anywhere.
            if band.isHidden():
                band.setGeometry(geo)
                self._start_vis_animator(self.band_target_opacity)
                self._rose.raise_()
            else:
                self._start_vis_animator(self.band_target_opacity)
                self._start_geo_animator(geo)
        else:
            self._start_vis_animator(0.0)

    def _start_vis_animator(self, opacity):
        """ (Re)start the visibility animator.

        Parameters
        ----------
        opacity : float
            The target opacity of the target object.

        """
        animator = self._vis_animator
        if animator.state() == animator.Running:
            animator.stop()
        target = animator.targetObject()
        if target.isHidden() and opacity != 0.0:
            target.setWindowOpacity(0.0)
            target.show()
        animator.setStartValue(target.windowOpacity())
        animator.setEndValue(opacity)
        animator.start()

    def _start_geo_animator(self, geo):
        """ (Re)start the visibility animator.

        Parameters
        ----------
        geo : QRect
            The target geometry for the target object.

        """
        animator = self._geo_animator
        if animator.state() == animator.Running:
            animator.stop()
        target = animator.targetObject()
        animator.setStartValue(target.geometry())
        animator.setEndValue(geo)
        animator.start()

    def _band_geometry(self, widget, guide):
        """ Compute the geometry for an overlay rubber band.

        Parameters
        ----------
        widget : QWidget
            The widget to which the band geometry should be fit.

        guide : Guide
            The rose guide under the mouse. This determines how the
            geometry of the band will be fit to the widget.

        """
        Guide = QGuideRose.Guide
        if guide == Guide.NoGuide:
            return QRect()

        # border hits
        border_size = self.border_size
        rect = widget.contentsRect()
        if guide == Guide.BorderNorth:
            rect.setHeight(border_size)
        elif guide == Guide.BorderEast:
            rect.setLeft(rect.right() + 1 - border_size)
        elif guide == Guide.BorderSouth:
            rect.setTop(rect.bottom() + 1 - border_size)
        elif guide == Guide.BorderWest:
            rect.setWidth(border_size)
        # For the next 4 conditions `widget` will be a QDockArea
        elif guide == Guide.BorderExNorth:
            bar_rect = widget.dockBarGeometry(QDockBar.North)
            if bar_rect.isValid():
                rect = bar_rect
            else:
                rect.setHeight(border_size / 2)
        elif guide == Guide.BorderExEast:
            bar_rect = widget.dockBarGeometry(QDockBar.East)
            if bar_rect.isValid():
                rect = bar_rect
            else:
                rect.setLeft(rect.right() + 1 - border_size / 2)
        elif guide == Guide.BorderExSouth:
            bar_rect = widget.dockBarGeometry(QDockBar.South)
            if bar_rect.isValid():
                rect = bar_rect
            else:
                rect.setTop(rect.bottom() + 1 - border_size / 2)
        elif guide == Guide.BorderExWest:
            bar_rect = widget.dockBarGeometry(QDockBar.West)
            if bar_rect.isValid():
                rect = bar_rect
            else:
                rect.setWidth(border_size / 2)

        # compass hits
        elif guide == Guide.CompassNorth:
            rect.setHeight(rect.height() / 3)
        elif guide == Guide.CompassEast:
            rect.setLeft(2 * rect.width() / 3)
        elif guide == Guide.CompassSouth:
            rect.setTop(2 * rect.height() / 3)
        elif guide == Guide.CompassWest:
            rect.setWidth(rect.width() / 3)
        elif guide == Guide.CompassCenter:
            pass  # nothing to do
        elif guide == Guide.CompassExNorth:
            pass  # nothing to do
        elif guide == Guide.CompassExEast:
            pass  # nothing to do
        elif guide == Guide.CompassExSouth:
            pass  # nothing to do
        elif guide == Guide.CompassExWest:
            pass  # nothing to do

        # splitter handle hits
        elif guide == Guide.SplitHorizontal:
            wo, r = divmod(border_size - rect.width(), 2)
            rect.setWidth(2 * (wo + r) + rect.width())
            rect.moveLeft(rect.x() - (wo + r))
        elif guide == Guide.SplitVertical:
            ho, r = divmod(border_size - widget.height(), 2)
            rect.setHeight(2 * (ho + r) + rect.height())
            rect.moveTop(rect.y() - (ho + r))

        # single center
        elif guide == Guide.AreaCenter:
            pass  # nothing to do

        # default no-op
        else:
            return QRect()

        pt = widget.mapToGlobal(rect.topLeft())
        return QRect(pt, rect.size())

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------
    def guide_at(self, pos):
        """ Get the dock guide for a given position.

        Parameters
        ----------
        pos : QPoint
            The position of interest, expressed in global coordinates.

        Returns
        -------
        result : Guide
            The guide enum which lies under the given point.

        """
        rose = self._rose
        pos = rose.mapFromGlobal(pos)
        return rose.guideAt(pos)

    def hide(self):
        """ Hide the overlay.

        This method will stop the timers and set the visibility of the
        guide rose and the rubber band to False.

        """
        self._rose_timer.stop()
        self._band_timer.stop()
        self._rose.hide()
        self._band.hide()

    def mouse_over_widget(self, widget, pos, empty=False):
        """ Update the overlays based on the mouse position.

        This handler should be invoked when the mouse hovers over a
        single widget (such as a floating dock container) as opposed to
        an area of docked widgets. The guide rose will be displayed in
        the center of the widget with no border guides.

        Parameters
        ----------
        widget : QWidget
            The widget under the mouse.

        pos : QPoint
            The hover position, expressed in the local coordinates of
            the widget.

        empty : bool, optional
            Whether the widget represents an empty widget. If this is
            True, a single center guide will be shown instead of the
            guide rose.

        """
        Mode = QGuideRose.Mode
        rose = self._rose
        target_mode = Mode.AreaCenter if empty else Mode.CompassEx
        self._target_rose_mode = target_mode
        if rose.mode() != target_mode:
            rose.setMode(Mode.NoMode)
            self._rose_timer.start(self.rose_delay)
            self._band_timer.start(self.band_delay)
        origin = widget.mapToGlobal(QPoint(0, 0))
        geo = QRect(origin, widget.size())
        dirty = rose.geometry() != geo
        if dirty:
            rose.hide()
            rose.setMode(Mode.NoMode)
            rose.setGeometry(geo)
        guide = rose.guideAt(pos, target_mode)
        if dirty or guide != self._last_guide:
            self._last_guide = guide
            self._target_band_geo = self._band_geometry(widget, guide)
            self._band_timer.start(self.band_delay)
        rose.setCenterPoint(QPoint(geo.width() / 2, geo.height() / 2))
        rose.mouseOver(pos)
        rose.show()

    def mouse_over_area(self, area, widget, pos):
        """ Update the overlays based on the mouse position.

        Parameters
        ----------
        area : QDockArea
            The dock area which contains the dock items onto which
            the overlay will be displayed.

        widget : QWidget
            The dock widget in the area which is under the mouse, or
            None if there is no relevant widget.

        pos : QPoint
            The hover position, expressed in the local coordinates of
            the overlayed dock area.

        """
        Mode = QGuideRose.Mode
        Guide = QGuideRose.Guide
        pane = area.centralPane()
        pos = pane.mapFrom(area, pos)

        if widget is None:
            if area.centralWidget() is None:
                self.mouse_over_widget(pane, pos, empty=True)
            return

        # Compute the target mode for the guide rose based on the dock
        # widget which lies under the mouse position.
        target_mode = Mode.Border
        if isinstance(widget, QDockContainer):
            target_mode |= Mode.CompassEx
        elif isinstance(widget, QDockTabWidget):
            target_mode |= Mode.Compass
        elif isinstance(widget, QDockSplitterHandle):
            if widget.orientation() == Qt.Horizontal:
                target_mode |= Mode.SplitHorizontal
            else:
                target_mode |= Mode.SplitVertical

        # Get the local area coordinates for the center of the widget.
        center = widget.mapTo(pane, QPoint(0, 0))
        center += QPoint(widget.width() / 2, widget.height() / 2)

        # Update the state of the rose. If it is to be hidden, it is
        # done so immediately. If the target mode is different from
        # the current mode, the rose is hidden and the state changes
        # are collapsed on a timer.
        rose = self._rose
        self._hover_pos = pos
        self._show_band = True
        self._target_rose_mode = target_mode
        if target_mode != rose.mode():
            rose.setMode(Mode.Border)
            self._rose_timer.start(self.rose_delay)
            self._show_band = False

        # Update the geometry of the rose if needed. This ensures that
        # the rose does not change geometry while visible.
        origin = pane.mapToGlobal(QPoint(0, 0))
        geo = QRect(origin, pane.size())
        dirty = rose.geometry() != geo
        if dirty:
            rose.hide()
            rose.setMode(Mode.NoMode)
            rose.setGeometry(geo)

        # Hit test the rose and update the target geometry for the
        # rubber band if the target guide has changed.
        rose.setCenterPoint(center)
        guide = rose.guideAt(pos, target_mode)
        if dirty or guide != self._last_guide:
            self._last_guide = guide
            if guide >= Guide.BorderNorth and guide <= Guide.BorderWest:
                band_geo = self._band_geometry(pane, guide)
            elif guide >= Guide.BorderExNorth and guide <= Guide.BorderExWest:
                band_geo = self._band_geometry(area, guide)
            else:
                band_geo = self._band_geometry(widget, guide)
            self._target_band_geo = band_geo
            self._band_timer.start(self.band_delay)

        # Finally, make the rose visible and issue a mouseover command
        # so that the guides are highlighted.
        rose.mouseOver(pos)
        rose.show()
Ejemplo n.º 24
0
class Field(Control):
    """ A single line editable text widget.

    """
    #: The text to display in the field.
    text = d_(Unicode())

    #: The mask to use for text input:
    #:  http://qt-project.org/doc/qt-4.8/qlineedit.html#inputMask-prop
    #:
    #: The summary of the mask grammar is as follows:
    #: A   ASCII alphabetic character required. A-Z, a-z.
    #: a   ASCII alphabetic character permitted but not required.
    #: N   ASCII alphanumeric character required. A-Z, a-z, 0-9.
    #: n   ASCII alphanumeric character permitted but not required.
    #: X   Any character required.
    #: x   Any character permitted but not required.
    #: 9   ASCII digit required. 0-9.
    #: 0   ASCII digit permitted but not required.
    #: D   ASCII digit required. 1-9.
    #: d   ASCII digit permitted but not required (1-9).
    #: #   ASCII digit or plus/minus sign permitted but not required.
    #: H   Hexadecimal character required. A-F, a-f, 0-9.
    #: h   Hexadecimal character permitted but not required.
    #: B   Binary character required. 0-1.
    #: b   Binary character permitted but not required.
    #: >   All following alphabetic characters are uppercased.
    #: <   All following alphabetic characters are lowercased.
    #: !   Switch off case conversion.
    #: \   Use \ to escape the special characters listed above to use them as separators.
    #:
    #: The mask consists of a string of mask characters and separators, optionally
    #: followed by a semicolon and the character used for blanks
    #: Eg: 9 digit phone number: (999) 999-9999;_
    mask = d_(Unicode())

    #: The validator to use for this field. If the validator provides
    #: a client side validator, then text will only be submitted if it
    #: passes that validator.
    validator = d_(Typed(Validator))

    #: The list of actions which should cause the client to submit its
    #: text to the server for validation and update. The currently
    #: supported values are 'lost_focus', 'return_pressed', and 'auto_sync'.
    #: The 'auto_sync' mode will attempt to validate and synchronize the
    #: text when the user stops typing.
    submit_triggers = d_(
        List(Enum('lost_focus', 'return_pressed', 'auto_sync'),
             ['lost_focus', 'return_pressed']))

    #: The grayed-out text to display if the field is empty and the
    #: widget doesn't have focus. Defaults to the empty string.
    placeholder = d_(Unicode())

    #: How to display the text in the field. Valid values are 'normal'
    #: which displays the text as normal, 'password' which displays the
    #: text with an obscured character, and 'silent' which displays no
    #: text at all but still allows input.
    echo_mode = d_(Enum('normal', 'password', 'silent'))

    #: The maximum length of the field in characters. The default value
    #: is Zero and indicates there is no maximum length.
    max_length = d_(Int(0))

    #: Whether or not the field is read only. Defaults to False.
    read_only = d_(Bool(False))

    #: How strongly a component hugs it's contents' width. Fields ignore
    #: the width hug by default, so they expand freely in width.
    hug_width = set_default('ignore')

    #: A reference to the ProxyField object.
    proxy = Typed(ProxyField)

    #--------------------------------------------------------------------------
    # Observers
    #--------------------------------------------------------------------------
    @observe('text', 'mask', 'submit_triggers', 'placeholder', 'echo_mode',
             'max_length', 'read_only')
    def _update_proxy(self, change):
        """ An observer which sends state change to the proxy.

        """
        # The superclass implementation is sufficient.
        super(Field, self)._update_proxy(change)

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------
    def field_text(self):
        """ Get the text stored in the field control.

        Depending on the state of the field, this text may be different
        than that stored in the 'text' attribute.

        Returns
        -------
        result : str
            The text stored in the field.

        """
        if self.proxy_is_active:
            return self.proxy.field_text()
        return ''
Ejemplo n.º 25
0
class QtSeparator(QtControl, ProxySeparator):
    """ A Qt implementation of an Enaml ProxySeparator.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QSeparator)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create underlying QSeparator control.

        """
        self.widget = QSeparator(self.parent_widget())

    def init_widget(self):
        """ Initialize the underlying widget.

        """
        super(QtSeparator, self).init_widget()
        d = self.declaration
        self.set_orientation(d.orientation, sh_guard=False)
        self.set_line_style(d.line_style, sh_guard=False)
        self.set_line_width(d.line_width, sh_guard=False)
        self.set_midline_width(d.midline_width, sh_guard=False)

    #--------------------------------------------------------------------------
    # Widget Update Methods
    #--------------------------------------------------------------------------
    def set_orientation(self, orientation, sh_guard=True):
        """ Set the orientation of the underlying widget.

        """
        if sh_guard:
            with size_hint_guard(self):
                self.widget.setFrameShape(LINE_SHAPES[orientation])
        else:
            self.widget.setFrameShape(LINE_SHAPES[orientation])

    def set_line_style(self, style, sh_guard=True):
        """ Set the line style of the underlying widget.

        """
        if sh_guard:
            with size_hint_guard(self):
                self.widget.setFrameShadow(LINE_STYLES[style])
        else:
            self.widget.setFrameShadow(LINE_STYLES[style])

    def set_line_width(self, width, sh_guard=True):
        """ Set the line width of the underlying widget.

        """
        if sh_guard:
            with size_hint_guard(self):
                self.widget.setLineWidth(width)
        else:
            self.widget.setLineWidth(width)
        self.widget.update()

    def set_midline_width(self, width, sh_guard=True):
        """ Set the midline width of the underlying widget.

        """
        if sh_guard:
            with size_hint_guard(self):
                self.widget.setMidLineWidth(width)
        else:
            self.widget.setMidLineWidth(width)
        self.widget.update()
Ejemplo n.º 26
0
class Race(FormulaModel):

    date = Typed(datetime.datetime)
    name = Unicode()
    round = Coerced(int)
    season = Coerced(int)
    url = Unicode()
    temp = Coerced(float)  #ToDo: should affect tires
    rain = Bool(default=False
                )  #ToDo: rain should affect prob wreck, safety car, car speeds

    circuit = Typed(Circuit)

    drivers = Property()
    _drivers = Typed(Drivers)

    laps = Property()
    _laps = Typed(Laps)

    standings = Property()
    _standings = Typed(Dict)

    def _get_drivers(self):
        if not self._drivers:
            query = {
                'year': self.season,
                'circuit_id': self.circuit.circuitId,
                'query_type': 'drivers'
            }
            self.drivers = self.api.query(**query)
        return self._drivers

    def _set_drivers(self, drivers):
        self._drivers = Drivers(drivers)

    def _get_laps(self):
        if not self._laps:
            query = {
                'year': self.season,
                'race_num': str(self.round),
                'query_type': 'laps'
            }
            tmp_season = self.api.query(**query)
            self.laps = tmp_season.races[0].laps

        return self._laps

    def _set_laps(self, laps):
        if not isinstance(laps, Laps):
            laps = Laps(laps, race=self)
        self._laps = laps

    def _get_standings(self):
        if not self._standings:
            query = {
                'year': self.season,
                'race_num': str(self.round),
                'query_type': 'driverStandings'
            }
            driver_standings = Standings(self.api.query(**query))

            query = {
                'year': self.season,
                'race_num': str(self.round),
                'query_type': 'constructorStandings'
            }
            constructor_standings = Standings(self.api.query(**query))

            standings_group = Dict()
            standings_group.drivers = driver_standings
            standings_group.constructors = constructor_standings
            self.standings = standings_group

        return self._standings

    def _set_standings(self, standings):
        self._standings = standings

    @classmethod
    def from_dict(cls, kwargs):
        date = kwargs.pop('date')
        if 'time' in kwargs.keys():
            race_time = kwargs.pop('time')
            kwargs['date'] = datetime.datetime.strptime(
                date + ' ' + race_time[:-1], '%Y-%m-%d %H:%M:%S')
        else:
            kwargs['date'] = datetime.datetime.strptime(date, '%Y-%m-%d')

        kwargs['circuit'] = Circuit.from_dict(kwargs.pop('Circuit'))
        kwargs['name'] = kwargs.pop('raceName')
        if 'Laps' in kwargs:
            kwargs['laps'] = [Lap.from_dict(lap) for lap in kwargs.pop('Laps')]
        return cls(**kwargs)

    @property
    def time(self):
        return self.date.time()

    def to_row(self):
        dict_props = ['date', 'name', 'round', 'season', 'time']
        cir = self.circuit.to_row()
        return dict({k: getattr(self, k)
                     for k in dict_props}.items() + cir.items())

    def __repr__(self):
        return ('%s-%s: %s') % (self.season, self.round, self.name)

    def __str__(self):
        name = variablize(self.name)
        return name + '_' + str(self.round)
Ejemplo n.º 27
0
class AndroidViewPager(AndroidViewGroup, ProxyViewPager):
    """ An Android implementation of an Enaml ProxyViewPager.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(ViewPager)

    #: Adapter
    adapter = Typed(BridgedFragmentStatePagerAdapter)

    #: Pending changes
    _notify_count = Int()
    _notify_delay = Int(2)
    _pending_calls = List()

    @property
    def pages(self):
        """ Get pages """
        #: Defer the import
        for p in self.declaration.pages:
            yield p.proxy

    # -------------------------------------------------------------------------
    # Initialization API
    # -------------------------------------------------------------------------
    def create_widget(self):
        """ Create the underlying widget.

        """
        self.widget = ViewPager(self.get_context())
        self.adapter = BridgedFragmentStatePagerAdapter()

    def init_layout(self):
        super(AndroidViewPager, self).init_layout()
        d = self.declaration
        w = self.widget

        #: Set adapter
        w.setAdapter(self.adapter)
        w.addOnPageChangeListener(w.getId())
        w.onPageSelected.connect(self.on_page_selected)

        if d.current_index:
            self.set_current_index(d.current_index)

    def child_added(self, child):
        """ When a child is added, schedule a data changed notification """
        super(AndroidViewPager, self).child_added(child)
        self._notify_count += 1
        self.get_context().timed_call(self._notify_delay, self._notify_change)

    def child_removed(self, child):
        """ When a child is removed, schedule a data changed notification """
        super(AndroidViewPager, self).child_removed(child)
        self._notify_count += 1
        self.get_context().timed_call(self._notify_delay, self._notify_change)

    def _notify_change(self):
        """ After all changes have settled, tell Java it changed """
        d = self.declaration
        self._notify_count -= 1
        if self._notify_count == 0:
            #: Tell the UI we made changes
            self.adapter.notifyDataSetChanged(now=True)
            self.get_context().timed_call(500, self._queue_pending_calls)

    def _queue_pending_calls(self):
        #: Now wait for current page to load, then invoke any pending calls
        for i, page in enumerate(self.pages):
            #: Wait for first page!
            #: Trigger when the current page is loaded
            page.ready.then(self._run_pending_calls)
            #: If the page is already complete it will be called right away
            break

    def _run_pending_calls(self, *args):
        if self._pending_calls:
            for call in self._pending_calls:
                call()
            self._pending_calls = []

    # -------------------------------------------------------------------------
    # OnItemRequestedListener API
    # -------------------------------------------------------------------------
    # def on_item_requested(self, position):
    #     print "on_item_requested"
    #     for i, c in enumerate(self.children()):
    #         if i == position:
    #             return c.widget

    # -------------------------------------------------------------------------
    # OnPageChangeListener API
    # -------------------------------------------------------------------------
    def on_page_scroll_state_changed(self, state):
        pass

    def on_page_scrolled(self, position, offset, offset_pixels):
        pass

    def on_page_selected(self, position):
        d = self.declaration
        with self.widget.setCurrentItem.suppressed():
            d.current_index = position

    # -------------------------------------------------------------------------
    # ProxyViewPager API
    # -------------------------------------------------------------------------
    def set_current_index(self, index):
        """ We can only set the index once the page has been created.
        otherwise we get `FragmentManager is already executing transactions`
        errors in Java. To avoid this, we only call this once has been loaded.
        
        """
        # d = self.declaration
        # #: We have to wait for the current_index to be ready before we can
        # #: change pages
        if self._notify_count > 0:
            self._pending_calls.append(
                lambda index=index: self.widget.setCurrentItem(index))
        else:
            self.widget.setCurrentItem(index)

    def set_offscreen_page_limit(self, limit):
        self.widget.setOffscreenPageLimit(limit)

    def set_page_margin(self, margin):
        self.widget.setPageMargin(margin)

    def set_paging_enabled(self, enabled):
        self.widget.setPagingEnabled(enabled)

    def set_transition(self, transition):
        self.widget.setPageTransformer(True,
                                       PageTransformer.from_name(transition))

    def create_layout_params(self, child, layout):
        """ Override as there is no (width, height) constructor.
        
        """
        from .android_fragment import AndroidFragment
        if isinstance(child, AndroidFragment):
            return super(AndroidViewPager,
                         self).create_layout_params(child, layout)
        # Only apply to decor views
        dp = self.dp
        w, h = (coerce_size(layout.get('width', 'match_parent')),
                coerce_size(layout.get('height', 'wrap_content')))
        w = w if w < 0 else int(w * dp)
        h = h if h < 0 else int(h * dp)
        # No (w,h) constructor
        params = ViewPagerLayoutParams()
        params.width = w
        params.height = h
        params.isDecor = True
        return params

    def apply_layout(self, child, layout):
        super(AndroidViewPager, self).apply_layout(child, layout)
        if 'gravity' in layout:
            child.layout_params.gravity = coerce_gravity(layout['gravity'])
Ejemplo n.º 28
0
class BottomSheetDialog(Dialog):
    """ A dialog that slides up from the bottom of the screen.
    
    """
    #: A reference to the proxy object.
    proxy = Typed(ProxyBottomSheetDialog)
Ejemplo n.º 29
0
class AndroidMapPolyline(AndroidMapItemBase, ProxyMapPolyline):
    """ An Android implementation of an Enaml ProxyMapPolyline.

    """

    #: Hold the points
    points = Typed(LatLngList)

    def create_widget(self):
        """ Create the MarkerOptions for this map marker
        this later gets converted into a "Marker" instance when addMarker 
        is called
        """
        self.options = PolylineOptions()
        #: List to hold our points
        self.points = LatLngList()

    def add_to_map(self, mapview):
        mapview.addPolyline(self.options).then(self.on_marker)

    def init_widget(self):
        super(AndroidMapPolyline, self).init_widget()
        d = self.declaration
        self.set_points(d.points)
        #if d.clickable:
        #    self.set_clickable(d.clickable)
        if d.color:
            self.set_color(d.color)
        if d.end_cap != 'butt':
            self.set_end_cap(d.end_cap)
        if d.start_cap != 'butt':
            self.set_start_cap(d.start_cap)
        if d.geodesic:
            self.set_geodesic(d.geodesic)
        if d.joint_type:
            self.set_joint_type(d.joint_type)
        if d.width != 10:
            self.set_width(d.width)

    # -------------------------------------------------------------------------
    # Polyline API
    # -------------------------------------------------------------------------
    def on_marker(self, mid):
        """ Convert our options into the actual marker object"""
        #mid, pos = marker
        self.marker = Polyline(__id__=mid)
        self.parent().markers[mid] = self
        self.marker.setTag(mid)

        d = self.declaration
        if d.clickable:
            self.set_clickable(d.clickable)

        #: Can free the options now
        del self.options

    def on_click(self):
        d = self.declaration
        d.clicked()

    # -------------------------------------------------------------------------
    # ProxyMapPolyline API
    # -------------------------------------------------------------------------
    def set_points(self, points):
        #: Have to hold on until after added to the ArrayList
        #: or the GC cleans them up and the bridge destroys them
        self.points.refresh_points(points)
        if self.marker:
            self.marker.setPoints(self.points)
        else:
            self.options.addAll(self.points)

    def update_points(self, change):
        """ Update the points in a smart way without passing them over the 
        bridge with every change.
        """
        #: Delegate to the special LatLngList
        self.points.handle_change(change)
        #: Trigger update
        self.marker.setPoints(self.points)

    def set_clickable(self, clickable):
        if self.marker:
            self.marker.setClickable(clickable)
        else:
            self.options.clickable(clickable)

    def set_color(self, color):
        if self.marker:
            self.marker.setColor(color)
        else:
            self.options.color(color)

    def set_end_cap(self, cap):
        if self.marker:
            self.marker.setEndCap(Polyline.CAPS[cap]())
        else:
            self.options.endCap(Polyline.CAPS[cap]())

    def set_geodesic(self, geodesic):
        if self.marker:
            self.marker.setGeodesic(geodesic)
        else:
            self.options.geodesic(geodesic)

    def set_joint_type(self, joint_type):
        if self.marker:
            self.marker.setJointType(Polyline.JOINT_TYPES[joint_type])
        else:
            self.options.jointType(Polyline.JOINT_TYPES[joint_type])

    def set_start_cap(self, cap):
        if self.marker:
            self.marker.setStartCap(Polyline.CAPS[cap]())
        else:
            self.options.startCap(Polyline.CAPS[cap]())

    def set_width(self, width):
        if self.marker:
            self.marker.setWidth(width)
        else:
            self.options.width(width)
Ejemplo n.º 30
0
class AndroidMapPolygon(AndroidMapItemBase, ProxyMapPolygon):
    """ An Android implementation of an Enaml ProxyMapPolygon.

    """

    #: Hold the points
    points = Typed(LatLngList)

    #: Hold the holes
    #holes = List(ArrayList)

    def create_widget(self):
        """ Create the MarkerOptions for this map marker
        this later gets converted into a "Marker" instance when addMarker 
        is called
        """
        self.options = PolygonOptions()
        self.points = LatLngList()

    def add_to_map(self, mapview):
        mapview.addPolygon(self.options).then(self.on_marker)

    def init_widget(self):
        super(AndroidMapPolygon, self).init_widget()
        d = self.declaration
        self.set_points(d.points)
        #if d.clickable:
        #    self.set_clickable(d.clickable)
        if d.fill_color:
            self.set_fill_color(d.fill_color)
        if d.geodesic:
            self.set_geodesic(d.geodesic)
        if d.stroke_joint_type:
            self.set_stroke_joint_type(d.joint_type)
        if d.stroke_color:
            self.set_stroke_color(d.stroke_color)
        if d.stroke_width != 10:
            self.set_stroke_width(d.stroke_width)

    # -------------------------------------------------------------------------
    # Marker API
    # -------------------------------------------------------------------------
    def on_marker(self, mid):
        """ Convert our options into the actual marker object"""
        #mid, pos = marker
        self.marker = Polygon(__id__=mid)
        self.parent().markers[mid] = self
        self.marker.setTag(mid)

        d = self.declaration
        if d.clickable:
            self.set_clickable(d.clickable)

        #: Can free the options now
        del self.options

    def on_click(self):
        d = self.declaration
        d.clicked()

    # -------------------------------------------------------------------------
    # ProxyMapMarker API
    # -------------------------------------------------------------------------
    def set_points(self, points):
        #: Have to hold on until after added to the ArrayList
        #: or the GC cleans them up and the bridge destroys them
        self.points.refresh_points(points)
        if self.marker:
            self.marker.setPoints(self.points)
        else:
            self.options.addAll(self.points)

    def update_points(self, change):
        #: Defer to points
        self.points.handle_change(change)
        #: Trigger update
        self.marker.setPoints(self.points)

    def set_clickable(self, clickable):
        if self.marker:
            self.marker.setClickable(clickable)
        else:
            self.options.clickable(clickable)

    def set_holes(self, holes):
        if self.marker:
            self.marker.setHoles(
                [bridge.encode(LatLng(*p)) for hole in holes for p in hole])
        else:
            for hole in holes:
                self.options.addHole([bridge.encode(LatLng(*p)) for p in hole])

    def set_fill_color(self, color):
        if self.marker:
            self.marker.setFillColor(color)
        else:
            self.options.fillColor(color)

    def set_geodesic(self, geodesic):
        if self.marker:
            self.marker.setGeodesic(geodesic)
        else:
            self.options.geodesic(geodesic)

    def set_stroke_color(self, color):
        if self.marker:
            self.marker.setStrokeColor(color)
        else:
            self.options.strokeColor(color)

    def set_stroke_joint_type(self, joint_type):
        if self.marker:
            self.marker.setStrokeJointType(Polyline.JOINT_TYPES[joint_type])
        else:
            self.options.strokeJointType(Polyline.JOINT_TYPES[joint_type])

    def set_stroke_width(self, width):
        if self.marker:
            self.marker.setStrokeWidth(width)
        else:
            self.options.strokeWidth(width)
Ejemplo n.º 31
0
class Looper(Pattern):
    """ A pattern object that repeats its children over an iterable.

    The children of a `Looper` are used as a template when creating new
    objects for each item in the given `iterable`. Each iteration of the
    loop will be given an indenpendent scope which is the union of the
    outer scope and any identifiers created during the iteration. This
    scope will also contain `loop_index` and `loop_item` variables which
    are the index and value of the iterable, respectively.

    All items created by the looper will be added as children of the
    parent of the `Looper`. The `Looper` keeps ownership of all items
    it creates. When the iterable for the looper is changed, the looper
    will only create and destroy children for the items in the iterable
    which have changed.

    """
    #: The iterable to use when creating the items for the looper.
    #: The items in the iterable must be unique. This allows the
    #: Looper to optimize the creation and destruction of widgets.
    iterable = d_(Instance(Iterable))

    #: The list of items created by the conditional. Each item in the
    #: list represents one iteration of the loop and is a list of the
    #: items generated during that iteration. This list should not be
    #: manipulated directly by user code.
    items = List()

    #: Private data storage which maps the user iterable data to the
    #: list of items created for that iteration. This allows the looper
    #: to only create and destroy the items which have changed.
    _iter_data = Typed(sortedmap, ())

    #--------------------------------------------------------------------------
    # Lifetime API
    #--------------------------------------------------------------------------
    def destroy(self):
        """ A reimplemented destructor.

        The looper will release the owned items on destruction.

        """
        super(Looper, self).destroy()
        del self.iterable
        del self.items
        del self._iter_data

    #--------------------------------------------------------------------------
    # Observers
    #--------------------------------------------------------------------------
    def _observe_iterable(self, change):
        """ A private observer for the `iterable` attribute.

        If the iterable changes while the looper is active, the loop
        items will be refreshed.

        """
        if change['type'] == 'update' and self.is_initialized:
            self.refresh_items()

    #--------------------------------------------------------------------------
    # Pattern API
    #--------------------------------------------------------------------------
    def pattern_items(self):
        """ Get a list of items created by the pattern.

        """
        return sum(self.items, [])

    def refresh_items(self):
        """ Refresh the items of the pattern.

        This method destroys the old items and creates and initializes
        the new items.

        """
        old_items = self.items[:]
        old_iter_data = self._iter_data
        iterable = self.iterable
        pattern_nodes = self.pattern_nodes
        new_iter_data = sortedmap()
        new_items = []

        if iterable is not None and len(pattern_nodes) > 0:
            for loop_index, loop_item in enumerate(iterable):
                iteration = old_iter_data.get(loop_item)
                if iteration is not None:
                    new_iter_data[loop_item] = iteration
                    new_items.append(iteration)
                    old_items.remove(iteration)
                    continue
                iteration = []
                new_iter_data[loop_item] = iteration
                new_items.append(iteration)
                for nodes, key, f_locals in pattern_nodes:
                    with new_scope(key, f_locals) as f_locals:
                        f_locals['loop_index'] = loop_index
                        f_locals['loop_item'] = loop_item
                        for node in nodes:
                            child = node(None)
                            if isinstance(child, list):
                                iteration.extend(child)
                            else:
                                iteration.append(child)

        for iteration in old_items:
            for old in iteration:
                if not old.is_destroyed:
                    old.destroy()

        if len(new_items) > 0:
            expanded = []
            recursive_expand(sum(new_items, []), expanded)
            self.parent.insert_children(self, expanded)

        self.items = new_items
        self._iter_data = new_iter_data