Exemple #1
0
class XViewTabMenu(XViewBaseMenu):
    def __init__(self, parent):
        self._pluginMenu = None
        self._groupingMenu = None
        self._panelGroup = QActionGroup(parent)
        self._groupingGroup = QActionGroup(parent)
        self._hideAction = None

        super(XViewTabMenu, self).__init__(parent)

    def setupActions(self):
        viewWidget = self.viewWidget()

        self.addSection('Views')
        self._pluginMenu = XViewPluginMenu(self.viewWidget())
        self._pluginMenu.setTitle('Switch View')
        self.addMenu(self._pluginMenu)
        self._pluginMenu.triggered.disconnect(self._pluginMenu.addView)
        self._pluginMenu.setIcon(QIcon(resources.find('img/view/switch.png')))
        self._pluginMenu.triggered.connect(self.swapTabType)

        # create view grouping options
        self._groupingMenu = self.addMenu('Set Group')
        icon = QIcon(resources.find('img/view/group.png'))
        self._groupingMenu.setIcon(icon)

        act = self._groupingMenu.addAction('No Grouping')
        act.setData(wrapVariant(0))
        act.setCheckable(True)
        self._groupingMenu.addSeparator()
        self._groupingGroup.addAction(act)

        for i in range(1, 6):
            act = self._groupingMenu.addAction('Group %i' % i)
            act.setData(wrapVariant(i))
            act.setCheckable(True)

            self._groupingGroup.addAction(act)

        self._groupingMenu.triggered.connect(self.assignGroup)

        self.setTitle('Tabs')
        self.addSection('Tabs')

        act = self.addAction('Rename Tab')
        act.setIcon(QIcon(resources.find('img/edit.png')))
        act.triggered.connect(self.renamePanel)

        act = self.addAction('Detach Tab')
        act.setIcon(QIcon(resources.find('img/view/detach.png')))
        act.triggered.connect(self.detachPanel)

        act = self.addAction('Detach Tab (as a Copy)')
        act.setIcon(QIcon(resources.find('img/view/detach_copy.png')))
        act.triggered.connect(self.detachPanelCopy)

        act = self.addAction('Close Tab')
        act.setIcon(QIcon(resources.find('img/view/tab_remove.png')))
        act.triggered.connect(self.closeView)

        self.addSeparator()

        act = self.addAction('Hide Tabs when Locked')
        act.triggered[bool].connect(self.setHideTabsWhenLocked)
        act.setCheckable(True)
        self._hideAction = act

        self.addSection('Panels')
        act = self.addAction('Split Panel Left/Right')
        act.setIcon(QIcon(resources.find('img/view/split_horizontal.png')))
        act.triggered.connect(self.splitHorizontal)

        split_adv = QAction(self)
        split_adv.setIcon(QIcon(resources.find('img/advanced.png')))
        split_adv.setToolTip('Specify number of columns for split')
        split_adv.triggered.connect(self.splitHorizontalAdvanced)
        self.setAdvancedAction(act, split_adv)

        act = self.addAction('Split Panel Top/Bottom')
        act.setIcon(QIcon(resources.find('img/view/split_vertical.png')))
        act.triggered.connect(self.splitVertical)

        split_adv = QAction(self)
        split_adv.setIcon(QIcon(resources.find('img/advanced.png')))
        split_adv.setToolTip('Specify number of rows for split')
        split_adv.triggered.connect(self.splitVerticalAdvanced)
        self.setAdvancedAction(act, split_adv)

    def setCurrentPanel(self, panel):
        super(XViewTabMenu, self).setCurrentPanel(panel)

        # update the current tab based on what view type it is
        viewType = ''
        grp = -1
        if panel is not None and panel.currentView():
            viewType = panel.currentView().viewName()
            grp = panel.currentView().viewingGroup()

        self._panelGroup.blockSignals(True)
        for act in self._panelGroup.actions():
            act.setChecked(viewType == act.text())
        self._panelGroup.blockSignals(False)

        self._groupingGroup.blockSignals(True)
        for act in self._groupingGroup.actions():
            act.setChecked(grp == unwrapVariant(act.data()))
        self._groupingGroup.blockSignals(False)

        self._pluginMenu.setCurrentPanel(panel)
        self._groupingMenu.setEnabled(grp != -1)

        if panel:
            self._hideAction.setChecked(panel.hideTabsWhenLocked())
            self._hideAction.setEnabled(True)
        else:
            self._hideAction.setEnabled(False)

    def setHideTabsWhenLocked(self, state):
        self.currentPanel().setHideTabsWhenLocked(state)
class XActionGroupWidget(QWidget):
    """
    ~~>[img:widgets/xactiongroupwidget.png]

    The XActionGroupWidget class provides a simple class for creating a 
    multi-checkable tool button based on QActions and QActionGroups.

    === Example Usage ===

    |>>> from projexui.widgets.xactiongroupwidget import XActionGroupWidget
    |>>> import projexui
    |
    |>>> # create the widget
    |>>> widget = projexui.testWidget(XActionGroupWidget)
    |
    |>>> # add some actions (can be text or a QAction)
    |>>> widget.addAction('Day')
    |>>> widget.addAction('Month')
    |>>> widget.addAction('Year')
    |
    |>>> # create connections
    |>>> def printAction(act): print act.text()
    |>>> widget.actionGroup().triggered.connect(printAction)
    """
    
    __designer_icon__ = projexui.resources.find('img/ui/multicheckbox.png')
    
    def __init__( self, parent = None ):
        super(XActionGroupWidget, self).__init__( parent )
        
        # define custom properties
        self._actionGroup   = None
        self._padding       = 5
        self._cornerRadius  = 10
        
        # set default properties
        layout = QBoxLayout(QBoxLayout.LeftToRight)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred )
        self.setLayout(layout)
        
        # create connections
    
    def actionGroup( self ):
        """
        Returns the action group linked with this widget.
        
        :return     <QActionGroup>
        """
        return self._actionGroup
    
    def addAction( self, action ):
        """
        Adds the inputed action to this widget's action group.  This will auto-\
        create a new group if no group is already defined.
        
        :param      action | <QAction> || <str>
        
        :return     <QAction>
        """
        if not isinstance(action, QAction):
            action_name = nativestring(action)
            action = QAction(action_name, self)
            action.setObjectName(action_name)
        
        action.setCheckable(True)
        
        if ( not self._actionGroup ):
            self._actionGroup = QActionGroup(self)
            action.setChecked(True)
            
        self._actionGroup.addAction(action)
        self.reset()
        return action
    
    def colorString( self, clr ):
        """
        Renders the inputed color to an RGB string value.
        
        :return     <str>
        """
        return 'rgb(%s, %s, %s)' % (clr.red(), clr.green(), clr.blue())
    
    def cornerRadius( self ):
        """
        Returns the corner radius for this widget.
        
        :return     <int>
        """
        return self._cornerRadius
    
    def currentAction( self ):
        """
        Returns the action that is currently checked in the system.
        
        :return     <QAction> || None
        """
        if ( not self._actionGroup ):
            return None
            
        for act in self._actionGroup.actions():
            if ( act.isChecked() ):
                return act
        return None
    
    def direction( self ):
        """
        Returns the direction for this widget.
        
        :return     <QBoxLayout::Direction>
        """
        return self.layout().direction()
    
    def findAction( self, text ):
        """
        Looks up the action based on the inputed text.
        
        :return     <QAction> || None
        """
        for action in self.actionGroup().actions():
            if ( text in (action.objectName(), action.text()) ):
                return action
        return None
    
    def padding( self ):
        """
        Returns the button padding amount for this widget.
        
        :return     <int>
        """
        return self._padding
    
    def reset( self ):
        """
        Resets the user interface buttons for this widget.
        """
        # clear previous widgets
        for btn in self.findChildren(QToolButton):
            btn.close()
            btn.setParent(None)
            btn.deleteLater()
        
        # determine coloring options
        palette             = self.palette()
        unchecked           = palette.color(palette.Button)
        
        # determine if this is a dark or light scheme
        avg = (unchecked.red() + unchecked.green() + unchecked.blue()) / 3.0
        if ( avg < 140 ):
            checked             = unchecked.lighter(115)
            
            checked_clr         = self.colorString(unchecked.lighter(120))
            border_clr          = self.colorString(unchecked.darker(140))
            unchecked_clr       = self.colorString(checked.lighter(140))
            unchecked_clr_alt   = self.colorString(checked.lighter(120))
            checked_clr_alt     = self.colorString(unchecked)
        else:
            checked             = unchecked.lighter(120)
            
            checked_clr         = self.colorString(unchecked)
            border_clr          = self.colorString(unchecked.darker(160))
            unchecked_clr       = self.colorString(checked)
            unchecked_clr_alt   = self.colorString(checked.darker(130))
            checked_clr_alt     = self.colorString(unchecked.darker(120))
        
        # define the stylesheet options
        options = {}
        options['top_left_radius']      = 0
        options['top_right_radius']     = 0
        options['bot_left_radius']      = 0
        options['bot_right_radius']     = 0
        options['border_color']         = border_clr
        options['checked_clr']          = checked_clr
        options['checked_clr_alt']      = checked_clr_alt
        options['unchecked_clr']        = unchecked_clr
        options['unchecked_clr_alt']    = unchecked_clr_alt
        options['padding_top']          = 1
        options['padding_bottom']       = 1
        options['padding_left']         = 1
        options['padding_right']        = 1
        
        horiz = self.direction() in (QBoxLayout.LeftToRight, 
                                     QBoxLayout.RightToLeft)
        
        if ( horiz ):
            options['x1'] = 0
            options['y1'] = 0
            options['x2'] = 0
            options['y2'] = 1
        else:
            options['x1'] = 0
            options['y1'] = 0
            options['x2'] = 1
            options['y2'] = 1
        
        actions = self.actionGroup().actions()
        count = len(actions)
        for i, action in enumerate(actions):
            btn = QToolButton(self)
            btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            btn.setDefaultAction(action)
            self.layout().insertWidget(i, btn)
            
            options['top_left_radius']  = 1
            options['bot_left_radius']  = 1
            options['top_right_radius'] = 1
            options['bot_right_radius'] = 1
            
            if ( horiz ):
                options['padding_left']     = self._padding
                options['padding_right']    = self._padding
            else:
                options['padding_top']      = self._padding
                options['padding_bottom']   = self._padding
            
            if ( not i ):
                if ( horiz ):
                    options['top_left_radius'] = self.cornerRadius()
                    options['bot_left_radius'] = self.cornerRadius()
                    options['padding_left']    += self.cornerRadius() / 3.0
                else:
                    options['top_left_radius'] = self.cornerRadius()
                    options['top_right_radius'] = self.cornerRadius()
                    options['padding_top']     += self.cornerRadius() / 3.0
                    
            elif ( i == count - 1 ):
                if ( horiz ):
                    options['top_right_radius'] = self.cornerRadius()
                    options['bot_right_radius'] = self.cornerRadius()
                    options['padding_right']    += self.cornerRadius() / 3.0
                else:
                    options['bot_left_radius']  = self.cornerRadius()
                    options['bot_right_radius'] = self.cornerRadius()
                    options['padding_bottom']   += self.cornerRadius() / 3.0
            
            btn.setStyleSheet(TOOLBUTTON_STYLE % options)
            btn.setAutoFillBackground(True)
    
    def setActionGroup( self, actionGroup ):
        """
        Sets the action group for this widget to the inputed action group.
        
        :param      actionGroup | <QActionGroup>
        """
        self._actionGroup = actionGroup
        self.reset()
    
    def setCornerRadius( self, radius ):
        """
        Sets the corner radius value for this widget to the inputed radius.
        
        :param      radius | <int>
        """
        self._cornerRadius = radius
    
    def setDirection( self, direction ):
        """
        Sets the direction that this group widget will face.
        
        :param      direction | <QBoxLayout::Direction>
        """
        self.layout().setDirection(direction)
        self.reset()
    
    def setPadding( self, padding ):
        """
        Sets the padding amount for this widget's button set.
        
        :param      padding | <int>
        """
        self._padding = padding
        self.reset()
Exemple #3
0
class XSplitButton(QWidget):
    """
    ~~>[img:widgets/xsplitbutton.png]

    The XSplitButton class provides a simple class for creating a 
    multi-checkable tool button based on QActions and QActionGroups.

    === Example Usage ===

    |>>> from projexui.widgets.xsplitbutton import XSplitButton
    |>>> import projexui
    |
    |>>> # create the widget
    |>>> widget = projexui.testWidget(XSplitButton)
    |
    |>>> # add some actions (can be text or a QAction)
    |>>> widget.addAction('Day')
    |>>> widget.addAction('Month')
    |>>> widget.addAction('Year')
    |
    |>>> # create connections
    |>>> def printAction(act): print act.text()
    |>>> widget.actionGroup().triggered.connect(printAction)
    """

    __designer_icon__ = projexui.resources.find('img/ui/multicheckbox.png')

    clicked = Signal()
    currentActionChanged = Signal(object)
    currentIndexChanged = Signal(int)
    hovered = Signal(object)
    triggered = Signal(object)

    def __init__(self, parent=None):
        super(XSplitButton, self).__init__(parent)

        # define custom properties
        self._actionGroup = QActionGroup(self)
        self._padding = 5
        self._cornerRadius = 10
        self._checkable = True

        # set default properties
        layout = QBoxLayout(QBoxLayout.LeftToRight)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setLayout(layout)
        self.clear()

        # create connections
        self._actionGroup.hovered.connect(self.emitHovered)
        self._actionGroup.triggered.connect(self.emitTriggered)

    def actions(self):
        """
        Returns a list of the actions linked with this widget.
        
        :return     [<QAction>, ..]
        """
        return self._actionGroup.actions()

    def actionTexts(self):
        """
        Returns a list of the action texts for this widget.
        
        :return     [<str>, ..]
        """
        return map(lambda x: x.text(), self._actionGroup.actions())

    def actionGroup(self):
        """
        Returns the action group linked with this widget.
        
        :return     <QActionGroup>
        """
        return self._actionGroup

    def addAction(self, action, checked=None, autoBuild=True):
        """
        Adds the inputed action to this widget's action group.  This will auto-\
        create a new group if no group is already defined.
        
        :param      action | <QAction> || <str>
        
        :return     <QAction>
        """
        # clear the holder
        actions = self._actionGroup.actions()
        if actions and actions[0].objectName() == 'place_holder':
            self._actionGroup.removeAction(actions[0])
            actions[0].deleteLater()

        # create an action from the name
        if not isinstance(action, QAction):
            action_name = nativestring(action)
            action = QAction(action_name, self)
            action.setObjectName(action_name)
            action.setCheckable(self.isCheckable())

            # auto-check the first option
            if checked or (not self._actionGroup.actions()
                           and checked is None):
                action.setChecked(True)

        elif self.isCheckable():
            action.setCheckable(True)
            if not self.currentAction():
                action.setChecked(True)

        self._actionGroup.addAction(action)

        if autoBuild:
            self.rebuild()

        return action

    def clear(self, autoBuild=True):
        """
        Clears the actions for this widget.
        """
        for action in self._actionGroup.actions():
            self._actionGroup.removeAction(action)

        action = QAction('', self)
        action.setObjectName('place_holder')
        self._actionGroup.addAction(action)
        if autoBuild:
            self.rebuild()

    def cornerRadius(self):
        """
        Returns the corner radius for this widget.
        
        :return     <int>
        """
        return self._cornerRadius

    def count(self):
        """
        Returns the number of actions associated with this button.
        
        :return     <int>
        """
        actions = self._actionGroup.actions()
        if len(actions) == 1 and actions[0].objectName() == 'place_holder':
            return 0

        return len(actions)

    def currentAction(self):
        """
        Returns the action that is currently checked in the system.
        
        :return     <QAction> || None
        """
        return self._actionGroup.checkedAction()

    def direction(self):
        """
        Returns the direction for this widget.
        
        :return     <QBoxLayout::Direction>
        """
        return self.layout().direction()

    def emitClicked(self):
        """
        Emits the clicked signal whenever any of the actions are clicked.
        """
        if not self.signalsBlocked():
            self.clicked.emit()

    def emitHovered(self, action):
        """
        Emits the hovered action for this widget.
        
        :param      action | <QAction>
        """
        if not self.signalsBlocked():
            self.hovered.emit(action)

    def emitTriggered(self, action):
        """
        Emits the triggered action for this widget.
        
        :param      action | <QAction>
        """
        self.currentActionChanged.emit(action)
        self.currentIndexChanged.emit(self.indexOf(action))

        if not self.signalsBlocked():
            self.triggered.emit(action)

    def findAction(self, text):
        """
        Looks up the action based on the inputed text.
        
        :return     <QAction> || None
        """
        for action in self.actionGroup().actions():
            if text in (action.objectName(), action.text()):
                return action
        return None

    def indexOf(self, action):
        """
        Returns the index of the inputed action.
        
        :param      action | <QAction> || None
        
        :return     <int>
        """
        for i, act in enumerate(self.actionGroup().actions()):
            if action in (act, act.objectName(), act.text()):
                return i
        return -1

    def isCheckable(self):
        """
        Returns whether or not the actions within this button should be
        checkable.
        
        :return     <bool>
        """
        return self._checkable

    def padding(self):
        """
        Returns the button padding amount for this widget.
        
        :return     <int>
        """
        return self._padding

    def rebuild(self):
        """
        Rebuilds the user interface buttons for this widget.
        """
        self.setUpdatesEnabled(False)

        # sync up the toolbuttons with our actions
        actions = self._actionGroup.actions()
        btns = self.findChildren(QToolButton)
        horiz = self.direction() in (QBoxLayout.LeftToRight,
                                     QBoxLayout.RightToLeft)

        # remove unnecessary buttons
        if len(actions) < len(btns):
            rem_btns = btns[len(actions) - 1:]
            btns = btns[:len(actions)]

            for btn in rem_btns:
                btn.close()
                btn.setParent(None)
                btn.deleteLater()

        # create new buttons
        elif len(btns) < len(actions):
            for i in range(len(btns), len(actions)):
                btn = QToolButton(self)
                btn.setAutoFillBackground(True)
                btns.append(btn)
                self.layout().addWidget(btn)

                btn.clicked.connect(self.emitClicked)

        # determine coloring options
        palette = self.palette()
        checked = palette.color(palette.Highlight)
        checked_fg = palette.color(palette.HighlightedText)
        unchecked = palette.color(palette.Button)
        unchecked_fg = palette.color(palette.ButtonText)
        border = palette.color(palette.Mid)

        # define the stylesheet options
        options = {}
        options['top_left_radius'] = 0
        options['top_right_radius'] = 0
        options['bot_left_radius'] = 0
        options['bot_right_radius'] = 0
        options['border_color'] = border.name()
        options['checked_fg'] = checked_fg.name()
        options['checked_bg'] = checked.name()
        options['checked_bg_alt'] = checked.darker(120).name()
        options['unchecked_fg'] = unchecked_fg.name()
        options['unchecked_bg'] = unchecked.name()
        options['unchecked_bg_alt'] = unchecked.darker(120).name()
        options['padding_top'] = 1
        options['padding_bottom'] = 1
        options['padding_left'] = 1
        options['padding_right'] = 1

        if horiz:
            options['x1'] = 0
            options['y1'] = 0
            options['x2'] = 0
            options['y2'] = 1
        else:
            options['x1'] = 0
            options['y1'] = 0
            options['x2'] = 1
            options['y2'] = 1

        # sync up the actions and buttons
        count = len(actions)
        palette = self.palette()
        font = self.font()
        for i, action in enumerate(actions):
            btn = btns[i]

            # assign the action for this button
            if btn.defaultAction() != action:
                # clear out any existing actions
                for act in btn.actions():
                    btn.removeAction(act)

                # assign the given action
                btn.setDefaultAction(action)

            options['top_left_radius'] = 1
            options['bot_left_radius'] = 1
            options['top_right_radius'] = 1
            options['bot_right_radius'] = 1

            if horiz:
                options['padding_left'] = self._padding
                options['padding_right'] = self._padding
            else:
                options['padding_top'] = self._padding
                options['padding_bottom'] = self._padding

            if not i:
                if horiz:
                    options['top_left_radius'] = self.cornerRadius()
                    options['bot_left_radius'] = self.cornerRadius()
                    options['padding_left'] += self.cornerRadius() / 3.0
                else:
                    options['top_left_radius'] = self.cornerRadius()
                    options['top_right_radius'] = self.cornerRadius()
                    options['padding_top'] += self.cornerRadius() / 3.0

            if i == count - 1:
                if horiz:
                    options['top_right_radius'] = self.cornerRadius()
                    options['bot_right_radius'] = self.cornerRadius()
                    options['padding_right'] += self.cornerRadius() / 3.0
                else:
                    options['bot_left_radius'] = self.cornerRadius()
                    options['bot_right_radius'] = self.cornerRadius()
                    options['padding_bottom'] += self.cornerRadius() / 3.0

            btn.setFont(font)
            btn.setPalette(palette)
            btn.setStyleSheet(TOOLBUTTON_STYLE % options)
            if horiz:
                btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            else:
                btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)

        self.setUpdatesEnabled(True)

    def setActions(self, actions):
        """
        Sets the actions for this widget to th inputed list of actions.
        
        :param      [<QAction>, ..]
        """
        self.clear(autoBuild=False)
        for action in actions:
            self.addAction(action, autoBuild=False)
        self.rebuild()

    def setActionTexts(self, names):
        """
        Convenience method for auto-generating actions based on text names,
        sets the list of actions for this widget to the inputed list of names.
        
        :param      names | [<str>, ..]
        """
        self.setActions(names)

    def setActionGroup(self, actionGroup):
        """
        Sets the action group for this widget to the inputed action group.
        
        :param      actionGroup | <QActionGroup>
        """
        self._actionGroup = actionGroup
        self.rebuild()

    def setCheckable(self, state):
        """
        Sets whether or not the actions within this button should be checkable.
        
        :param      state | <bool>
        """
        self._checkable = state
        for act in self._actionGroup.actions():
            act.setCheckable(state)

    def setCornerRadius(self, radius):
        """
        Sets the corner radius value for this widget to the inputed radius.
        
        :param      radius | <int>
        """
        self._cornerRadius = radius

    def setCurrentAction(self, action):
        """
        Sets the current action for this button to the inputed action.
        
        :param      action | <QAction> || <str>
        """
        self._actionGroup.blockSignals(True)
        for act in self._actionGroup.actions():
            act.setChecked(act == action or act.text() == action)
        self._actionGroup.blockSignals(False)

    def setDirection(self, direction):
        """
        Sets the direction that this group widget will face.
        
        :param      direction | <QBoxLayout::Direction>
        """
        self.layout().setDirection(direction)
        self.rebuild()

    def setFont(self, font):
        """
        Sets the font for this widget and propogates down to the buttons.
        
        :param      font | <QFont>
        """
        super(XSplitButton, self).setFont(font)
        self.rebuild()

    def setPadding(self, padding):
        """
        Sets the padding amount for this widget's button set.
        
        :param      padding | <int>
        """
        self._padding = padding
        self.rebuild()

    def setPalette(self, palette):
        """
        Rebuilds the buttons for this widget since they use specific palette
        options.
        
        :param      palette | <QPalette>
        """
        super(XSplitButton, self).setPalette(palette)
        self.rebuild()

    def sizeHint(self):
        """
        Returns the base size hint for this widget.
        
        :return     <QSize>
        """
        return QSize(35, 22)

    x_actionTexts = Property(QStringList, actionTexts, setActionTexts)
    x_checkable = Property(bool, isCheckable, setCheckable)
class XViewTabMenu(XViewBaseMenu):
    def __init__(self, parent):
        self._pluginMenu = None
        self._groupingMenu = None
        self._panelGroup    = QActionGroup(parent)
        self._groupingGroup = QActionGroup(parent)
        self._hideAction = None
        
        super(XViewTabMenu, self).__init__(parent)
        
    def setupActions(self):
        viewWidget = self.viewWidget()
        
        self.addSection('Views')
        self._pluginMenu = XViewPluginMenu(self.viewWidget())
        self._pluginMenu.setTitle('Switch View')
        self.addMenu(self._pluginMenu)
        self._pluginMenu.triggered.disconnect(self._pluginMenu.addView)
        self._pluginMenu.setIcon(QIcon(resources.find('img/view/switch.png')))
        self._pluginMenu.triggered.connect(self.swapTabType)
        
        # create view grouping options
        self._groupingMenu = self.addMenu('Set Group')
        icon = QIcon(resources.find('img/view/group.png'))
        self._groupingMenu.setIcon(icon)
        
        act = self._groupingMenu.addAction('No Grouping')
        act.setData(wrapVariant(0))
        act.setCheckable(True)
        self._groupingMenu.addSeparator()
        self._groupingGroup.addAction(act)
        
        for i in range(1, 6):
            act = self._groupingMenu.addAction('Group %i' % i)
            act.setData(wrapVariant(i))
            act.setCheckable(True)
            
            self._groupingGroup.addAction(act)
        
        self._groupingMenu.triggered.connect(self.assignGroup)
        
        self.setTitle('Tabs')
        self.addSection('Tabs')
        
        act = self.addAction('Rename Tab')
        act.setIcon(QIcon(resources.find('img/edit.png')))
        act.triggered.connect( self.renamePanel )
        
        act = self.addAction('Detach Tab')
        act.setIcon(QIcon(resources.find('img/view/detach.png')))
        act.triggered.connect( self.detachPanel )
        
        act = self.addAction('Detach Tab (as a Copy)')
        act.setIcon(QIcon(resources.find('img/view/detach_copy.png')))
        act.triggered.connect( self.detachPanelCopy )
        
        act = self.addAction('Close Tab')
        act.setIcon(QIcon(resources.find('img/view/tab_remove.png')))
        act.triggered.connect( self.closeView )
        
        self.addSeparator()
        
        act = self.addAction('Hide Tabs when Locked')
        act.triggered[bool].connect(self.setHideTabsWhenLocked)
        act.setCheckable(True)
        self._hideAction = act
        
        self.addSection('Panels')
        act = self.addAction('Split Panel Left/Right')
        act.setIcon(QIcon(resources.find('img/view/split_horizontal.png')))
        act.triggered.connect(self.splitHorizontal)
        
        split_adv = QAction(self)
        split_adv.setIcon(QIcon(resources.find('img/advanced.png')))
        split_adv.setToolTip('Specify number of columns for split')
        split_adv.triggered.connect(self.splitHorizontalAdvanced)
        self.setAdvancedAction(act, split_adv)
        
        act = self.addAction('Split Panel Top/Bottom')
        act.setIcon(QIcon(resources.find('img/view/split_vertical.png')))
        act.triggered.connect(self.splitVertical)
    
        split_adv = QAction(self)
        split_adv.setIcon(QIcon(resources.find('img/advanced.png')))
        split_adv.setToolTip('Specify number of rows for split')
        split_adv.triggered.connect(self.splitVerticalAdvanced)
        self.setAdvancedAction(act, split_adv)
        
    def setCurrentPanel(self, panel):
        super(XViewTabMenu, self).setCurrentPanel(panel)
        
        # update the current tab based on what view type it is
        viewType = ''
        grp = -1
        if panel is not None and panel.currentView():
            viewType = panel.currentView().viewName()
            grp = panel.currentView().viewingGroup()
        
        self._panelGroup.blockSignals(True)
        for act in self._panelGroup.actions():
            act.setChecked(viewType == act.text())
        self._panelGroup.blockSignals(False)
        
        self._groupingGroup.blockSignals(True)
        for act in self._groupingGroup.actions():
            act.setChecked(grp == unwrapVariant(act.data()))
        self._groupingGroup.blockSignals(False)
        
        self._pluginMenu.setCurrentPanel(panel)
        self._groupingMenu.setEnabled(grp != -1)
        
        if panel:
            self._hideAction.setChecked(panel.hideTabsWhenLocked())
            self._hideAction.setEnabled(True)
        else:
            self._hideAction.setEnabled(False)

    def setHideTabsWhenLocked(self, state):
        self.currentPanel().setHideTabsWhenLocked(state)
class XViewProfileToolBar(XToolBar):
    profileCreated              = Signal(PyObject)
    profileChanged              = Signal(PyObject)
    profileRemoved              = Signal(PyObject)
    profilesChanged             = Signal()
    currentProfileChanged       = Signal(PyObject)
    loadProfileFinished         = Signal(PyObject)
    newWindowProfileRequested   = Signal(PyObject)
    
    def __init__(self, parent):
        super(XViewProfileToolBar, self).__init__(parent)
        
        # create custom properties
        self._editingEnabled    = True
        self._viewWidget        = None
        self._profileText       = 'Profile'
        self._profileGroup      = QActionGroup(self)
        self._currentProfile    = None
        
        # set the default options
        self.setIconSize(QSize(48, 48))
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        
        # create connections
        self.actionTriggered.connect(self.handleActionTrigger)
        self.customContextMenuRequested.connect(self.showProfileMenu)
        
    def addProfile(self, profile):
        """
        Adds the inputed profile as an action to the toolbar.
        
        :param      profile | <projexui.widgets.xviewwidget.XViewProfile>
        """
        # use the existing version
        for exist in self.profiles():
            if exist.name() == profile.name():
                if exist.version() < profile.version():
                    exist.setProfile(profile)
                
                return
        
        # otherwise create a new profile 
        act = XViewProfileAction(profile, self)
        self._profileGroup.addAction(act)
        self.addAction(act)
        return act
    
    def clearActive(self):
        # clear the GUI
        self.blockSignals(True)
        for act in self.actions():
            act.blockSignals(True)
            act.setChecked(False)
            act.blockSignals(False)
        self.blockSignals(False)
        
        self._currentProfile = None
        
        # reset the layout
        widget = self.viewWidget()
        if self.sender() != widget:
            widget.reset(force=True)
            widget.setLocked(False)
        
        self.currentProfileChanged.emit(None)

    def currentProfile(self):
        """
        Returns the current profile for this toolbar.
        
        :return     <projexui.widgets.xviewwidget.XViewProfile> || None
        """
        return self._currentProfile
    
    def createProfile(self, profile=None, clearLayout=True):
        """
        Prompts the user to create a new profile.
        """
        if profile:
            prof = profile
        elif not self.viewWidget() or clearLayout:
            prof = XViewProfile()
        else:
            prof = self.viewWidget().saveProfile()
        
        blocked = self.signalsBlocked()
        self.blockSignals(False)
        changed = self.editProfile(prof)
        self.blockSignals(blocked)
        
        if not changed:
            return
        
        act = self.addProfile(prof)
        act.setChecked(True)
        
        # update the interface
        if self.viewWidget() and (profile or clearLayout):
            self.viewWidget().restoreProfile(prof)
        
        if not self.signalsBlocked():
            self.profileCreated.emit(prof)
            self.profilesChanged.emit()
    
    @Slot(PyObject)
    def editProfile(self, profile):
        """
        Prompts the user to edit the given profile.
        
        :param      profile | <projexui.widgets.xviewwidget.XViewProfile>
        """
        mod = XViewProfileDialog.edit(self.window(), profile)
        if not mod:
            return False
        
        # update the action interface
        for act in self._profileGroup.actions():
            if act.profile() == profile:
                act.setProfile(profile)
                break
        
        # signal the change
        if not self.signalsBlocked():
            self.profileChanged.emit(profile)
            self.profilesChanged.emit()
        
        return True
    
    def exportProfile(self, profile, filename=None):
        """
        Exports this toolbar to the given filename.
        
        :param      profile  | <XViewProfile>
                    filename | <str> || None
        """
        if not filename:
            filename = QFileDialog.getSaveFileName(self,
                                                   'Export Profile',
                                                   '',
                                                   'XML Files (*.xml)')
            if type(filename) == tuple:
                filename = nativestring(filename[0])
        
        if not filename:
            return False
        
        profile.save(filename)
        return True
    
    def handleActionTrigger(self, action):
        """
        Handles when an action has been triggered.  If the inputed action is a 
        XViewProfileAction, then the currentProfileChanged signal will emit.
        
        :param      action | <QAction>
        """
        # trigger a particular profile
        if isinstance(action, XViewProfileAction):
            # if the user CTRL+Clicks on the action, then attempt
            # to load it in a new window
            if QApplication.keyboardModifiers() == Qt.ControlModifier:
                self.newWindowProfileRequested.emit(action.profile())
                self.setCurrentProfile(self.currentProfile())
                return
            else:
                self.setCurrentProfile(action.profile())
    
    def importProfile(self, filename=None):
        """
        Imports the profiles from the given filename.
        
        :param      filename | <str> || None
        """
        if not filename:
            filename = QFileDialog.getOpenFileName( self,
                                                    'Import Perspective',
                                                    '',
                                                    'XML Files (*.xml)')
            
            if type(filename) == tuple:
                filename = nativestring(filename[0])
            
        if not (filename and os.path.exists(filename)):
            return False
        
        prof = XViewProfile.load(filename)
        if prof:
            self.addProfile(prof)
    
    def isEditingEnabled(self):
        """
        Sets whether or not the create is enabled for this toolbar.
        
        :return     <bool>
        """
        return self._editingEnabled
    
    def isEmpty(self):
        """
        Returns whether or not this toolbar is empty.
        
        :return     <bool>
        """
        return len(self._profileGroup.actions()) == 0
    
    def loadString(self, profilestr, merge=False, loadProfile=True):
        """
        Loads the information for this toolbar from the inputed string.
        
        :param      profilestr | <str>
        """
        try:
            xtoolbar = ElementTree.fromstring(nativestring(profilestr))
        except ExpatError, e:
            return
        
        if not merge:
            self.clear()
        
        self.blockSignals(True)
        for xprofile in xtoolbar:
            prof = XViewProfile.fromXml(xprofile)
            
            if merge:
                self.removeProfile(prof.name(), silent=True)
            
            self.addProfile(prof)

        self.setCurrentProfile(xtoolbar.get('current'))
        
        self.blockSignals(False)
        self.profilesChanged.emit()
Exemple #6
0
class XSplitButton(QWidget):
    """
    ~~>[img:widgets/xsplitbutton.png]

    The XSplitButton class provides a simple class for creating a 
    multi-checkable tool button based on QActions and QActionGroups.

    === Example Usage ===

    |>>> from projexui.widgets.xsplitbutton import XSplitButton
    |>>> import projexui
    |
    |>>> # create the widget
    |>>> widget = projexui.testWidget(XSplitButton)
    |
    |>>> # add some actions (can be text or a QAction)
    |>>> widget.addAction('Day')
    |>>> widget.addAction('Month')
    |>>> widget.addAction('Year')
    |
    |>>> # create connections
    |>>> def printAction(act): print act.text()
    |>>> widget.actionGroup().triggered.connect(printAction)
    """
    
    __designer_icon__ = projexui.resources.find('img/ui/multicheckbox.png')
    
    clicked                 = Signal()
    currentActionChanged    = Signal(object)
    currentIndexChanged     = Signal(int)
    hovered                 = Signal(object)
    triggered               = Signal(object)
    
    def __init__( self, parent = None ):
        super(XSplitButton, self).__init__( parent )
        
        # define custom properties
        self._actionGroup   = QActionGroup(self)
        self._padding       = 5
        self._cornerRadius  = 10
        self._checkable     = True
        
        # set default properties
        layout = QBoxLayout(QBoxLayout.LeftToRight)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setLayout(layout)
        self.clear()
        
        # create connections
        self._actionGroup.hovered.connect(self.emitHovered)
        self._actionGroup.triggered.connect(self.emitTriggered)
    
    def actions(self):
        """
        Returns a list of the actions linked with this widget.
        
        :return     [<QAction>, ..]
        """
        return self._actionGroup.actions()
    
    def actionTexts(self):
        """
        Returns a list of the action texts for this widget.
        
        :return     [<str>, ..]
        """
        return map(lambda x: x.text(), self._actionGroup.actions())
    
    def actionGroup(self):
        """
        Returns the action group linked with this widget.
        
        :return     <QActionGroup>
        """
        return self._actionGroup
    
    def addAction(self, action, checked=None, autoBuild=True):
        """
        Adds the inputed action to this widget's action group.  This will auto-\
        create a new group if no group is already defined.
        
        :param      action | <QAction> || <str>
        
        :return     <QAction>
        """
        # clear the holder
        actions = self._actionGroup.actions()
        if actions and actions[0].objectName() == 'place_holder':
            self._actionGroup.removeAction(actions[0])
            actions[0].deleteLater()
        
        # create an action from the name
        if not isinstance(action, QAction):
            action_name = nativestring(action)
            action = QAction(action_name, self)
            action.setObjectName(action_name)
            action.setCheckable(self.isCheckable())
            
            # auto-check the first option
            if checked or (not self._actionGroup.actions() and checked is None):
                action.setChecked(True)
        
        elif self.isCheckable():
            action.setCheckable(True)
            if not self.currentAction():
                action.setChecked(True)
        
        self._actionGroup.addAction(action)
        
        if autoBuild:
            self.rebuild()
        
        return action
    
    def clear(self, autoBuild=True):
        """
        Clears the actions for this widget.
        """
        for action in self._actionGroup.actions():
            self._actionGroup.removeAction(action)
        
        action = QAction('', self)
        action.setObjectName('place_holder')
        self._actionGroup.addAction(action)
        if autoBuild:
            self.rebuild()
    
    def cornerRadius( self ):
        """
        Returns the corner radius for this widget.
        
        :return     <int>
        """
        return self._cornerRadius
    
    def count(self):
        """
        Returns the number of actions associated with this button.
        
        :return     <int>
        """
        actions = self._actionGroup.actions()
        if len(actions) == 1 and actions[0].objectName() == 'place_holder':
            return 0
        
        return len(actions)
    
    def currentAction(self):
        """
        Returns the action that is currently checked in the system.
        
        :return     <QAction> || None
        """
        return self._actionGroup.checkedAction()
    
    def direction( self ):
        """
        Returns the direction for this widget.
        
        :return     <QBoxLayout::Direction>
        """
        return self.layout().direction()
    
    def emitClicked(self):
        """
        Emits the clicked signal whenever any of the actions are clicked.
        """
        if not self.signalsBlocked():
            self.clicked.emit()
    
    def emitHovered(self, action):
        """
        Emits the hovered action for this widget.
        
        :param      action | <QAction>
        """
        if not self.signalsBlocked():
            self.hovered.emit(action)
    
    def emitTriggered(self, action):
        """
        Emits the triggered action for this widget.
        
        :param      action | <QAction>
        """
        self.currentActionChanged.emit(action)
        self.currentIndexChanged.emit(self.indexOf(action))
        
        if not self.signalsBlocked():
            self.triggered.emit(action)
    
    def findAction(self, text):
        """
        Looks up the action based on the inputed text.
        
        :return     <QAction> || None
        """
        for action in self.actionGroup().actions():
            if text in (action.objectName(), action.text()):
                return action
        return None
    
    def indexOf(self, action):
        """
        Returns the index of the inputed action.
        
        :param      action | <QAction> || None
        
        :return     <int>
        """
        for i, act in enumerate(self.actionGroup().actions()):
            if action in (act, act.objectName(), act.text()):
                return i
        return -1
    
    def isCheckable(self):
        """
        Returns whether or not the actions within this button should be
        checkable.
        
        :return     <bool>
        """
        return self._checkable
    
    def padding( self ):
        """
        Returns the button padding amount for this widget.
        
        :return     <int>
        """
        return self._padding
    
    def rebuild( self ):
        """
        Rebuilds the user interface buttons for this widget.
        """
        self.setUpdatesEnabled(False)
        
        # sync up the toolbuttons with our actions
        actions = self._actionGroup.actions()
        btns    = self.findChildren(QToolButton)
        horiz   = self.direction() in (QBoxLayout.LeftToRight, 
                                       QBoxLayout.RightToLeft)
        
        # remove unnecessary buttons
        if len(actions) < len(btns):
            rem_btns = btns[len(actions)-1:]
            btns = btns[:len(actions)]
            
            for btn in rem_btns:
                btn.close()
                btn.setParent(None)
                btn.deleteLater()
        
        # create new buttons
        elif len(btns) < len(actions):
            for i in range(len(btns), len(actions)):
                btn = QToolButton(self)
                btn.setAutoFillBackground(True)
                btns.append(btn)
                self.layout().addWidget(btn)
                
                btn.clicked.connect(self.emitClicked)
        
        # determine coloring options
        palette      = self.palette()
        checked      = palette.color(palette.Highlight)
        checked_fg   = palette.color(palette.HighlightedText)
        unchecked    = palette.color(palette.Button)
        unchecked_fg = palette.color(palette.ButtonText)
        border       = palette.color(palette.Mid)
        
        # define the stylesheet options
        options = {}
        options['top_left_radius']      = 0
        options['top_right_radius']     = 0
        options['bot_left_radius']      = 0
        options['bot_right_radius']     = 0
        options['border_color']         = border.name()
        options['checked_fg']           = checked_fg.name()
        options['checked_bg']           = checked.name()
        options['checked_bg_alt']       = checked.darker(120).name()
        options['unchecked_fg']         = unchecked_fg.name()
        options['unchecked_bg']         = unchecked.name()
        options['unchecked_bg_alt']     = unchecked.darker(120).name()
        options['padding_top']          = 1
        options['padding_bottom']       = 1
        options['padding_left']         = 1
        options['padding_right']        = 1
        
        if horiz:
            options['x1'] = 0
            options['y1'] = 0
            options['x2'] = 0
            options['y2'] = 1
        else:
            options['x1'] = 0
            options['y1'] = 0
            options['x2'] = 1
            options['y2'] = 1
        
        # sync up the actions and buttons
        count = len(actions)
        palette = self.palette()
        font = self.font()
        for i, action in enumerate(actions):
            btn = btns[i]
            
            # assign the action for this button
            if btn.defaultAction() != action:
                # clear out any existing actions
                for act in btn.actions():
                    btn.removeAction(act)
                
                # assign the given action
                btn.setDefaultAction(action)
            
            options['top_left_radius']  = 1
            options['bot_left_radius']  = 1
            options['top_right_radius'] = 1
            options['bot_right_radius'] = 1
            
            if horiz:
                options['padding_left']     = self._padding
                options['padding_right']    = self._padding
            else:
                options['padding_top']      = self._padding
                options['padding_bottom']   = self._padding
            
            if not i:
                if horiz:
                    options['top_left_radius'] = self.cornerRadius()
                    options['bot_left_radius'] = self.cornerRadius()
                    options['padding_left']    += self.cornerRadius() / 3.0
                else:
                    options['top_left_radius'] = self.cornerRadius()
                    options['top_right_radius'] = self.cornerRadius()
                    options['padding_top']     += self.cornerRadius() / 3.0
                    
            if i == count - 1:
                if horiz:
                    options['top_right_radius'] = self.cornerRadius()
                    options['bot_right_radius'] = self.cornerRadius()
                    options['padding_right']    += self.cornerRadius() / 3.0
                else:
                    options['bot_left_radius']  = self.cornerRadius()
                    options['bot_right_radius'] = self.cornerRadius()
                    options['padding_bottom']   += self.cornerRadius() / 3.0
            
            btn.setFont(font)
            btn.setPalette(palette)
            btn.setStyleSheet(TOOLBUTTON_STYLE % options)
            if horiz:
                btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            else:
                btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        
        self.setUpdatesEnabled(True)
    
    def setActions(self, actions):
        """
        Sets the actions for this widget to th inputed list of actions.
        
        :param      [<QAction>, ..]
        """
        self.clear(autoBuild=False)
        for action in actions:
            self.addAction(action, autoBuild=False)
        self.rebuild()
    
    def setActionTexts(self, names):
        """
        Convenience method for auto-generating actions based on text names,
        sets the list of actions for this widget to the inputed list of names.
        
        :param      names | [<str>, ..]
        """
        self.setActions(names)
    
    def setActionGroup( self, actionGroup ):
        """
        Sets the action group for this widget to the inputed action group.
        
        :param      actionGroup | <QActionGroup>
        """
        self._actionGroup = actionGroup
        self.rebuild()
    
    def setCheckable(self, state):
        """
        Sets whether or not the actions within this button should be checkable.
        
        :param      state | <bool>
        """
        self._checkable = state
        for act in self._actionGroup.actions():
            act.setCheckable(state)
    
    def setCornerRadius( self, radius ):
        """
        Sets the corner radius value for this widget to the inputed radius.
        
        :param      radius | <int>
        """
        self._cornerRadius = radius
    
    def setCurrentAction(self, action):
        """
        Sets the current action for this button to the inputed action.
        
        :param      action | <QAction> || <str>
        """
        self._actionGroup.blockSignals(True)
        for act in self._actionGroup.actions():
            act.setChecked(act == action or act.text() == action)
        self._actionGroup.blockSignals(False)
    
    def setDirection( self, direction ):
        """
        Sets the direction that this group widget will face.
        
        :param      direction | <QBoxLayout::Direction>
        """
        self.layout().setDirection(direction)
        self.rebuild()
    
    def setFont(self, font):
        """
        Sets the font for this widget and propogates down to the buttons.
        
        :param      font | <QFont>
        """
        super(XSplitButton, self).setFont(font)
        self.rebuild()
    
    def setPadding( self, padding ):
        """
        Sets the padding amount for this widget's button set.
        
        :param      padding | <int>
        """
        self._padding = padding
        self.rebuild()
    
    def setPalette(self, palette):
        """
        Rebuilds the buttons for this widget since they use specific palette
        options.
        
        :param      palette | <QPalette>
        """
        super(XSplitButton, self).setPalette(palette)
        self.rebuild()
    
    def sizeHint(self):
        """
        Returns the base size hint for this widget.
        
        :return     <QSize>
        """
        return QSize(35, 22)
    
    x_actionTexts = Property(QStringList, actionTexts, setActionTexts)
    x_checkable   = Property(bool, isCheckable, setCheckable)
Exemple #7
0
class XViewProfileToolBar(XToolBar):
    profileCreated = Signal(PyObject)
    profileChanged = Signal(PyObject)
    profileRemoved = Signal(PyObject)
    profilesChanged = Signal()
    currentProfileChanged = Signal(PyObject)
    loadProfileFinished = Signal(PyObject)
    newWindowProfileRequested = Signal(PyObject)

    def __init__(self, parent):
        super(XViewProfileToolBar, self).__init__(parent)

        # create custom properties
        self._editingEnabled = True
        self._viewWidget = None
        self._profileText = 'Profile'
        self._profileGroup = QActionGroup(self)
        self._currentProfile = None

        # set the default options
        self.setIconSize(QSize(48, 48))
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.setContextMenuPolicy(Qt.CustomContextMenu)

        # create connections
        self.actionTriggered.connect(self.handleActionTrigger)
        self.customContextMenuRequested.connect(self.showProfileMenu)

    def addProfile(self, profile):
        """
        Adds the inputed profile as an action to the toolbar.
        
        :param      profile | <projexui.widgets.xviewwidget.XViewProfile>
        """
        # use the existing version
        for exist in self.profiles():
            if exist.name() == profile.name():
                if exist.version() < profile.version():
                    exist.setProfile(profile)

                return

        # otherwise create a new profile
        act = XViewProfileAction(profile, self)
        self._profileGroup.addAction(act)
        self.addAction(act)
        return act

    def clearActive(self):
        # clear the GUI
        self.blockSignals(True)
        for act in self.actions():
            act.blockSignals(True)
            act.setChecked(False)
            act.blockSignals(False)
        self.blockSignals(False)

        self._currentProfile = None

        # reset the layout
        widget = self.viewWidget()
        if self.sender() != widget:
            widget.reset(force=True)
            widget.setLocked(False)

        self.currentProfileChanged.emit(None)

    def currentProfile(self):
        """
        Returns the current profile for this toolbar.
        
        :return     <projexui.widgets.xviewwidget.XViewProfile> || None
        """
        return self._currentProfile

    def createProfile(self, profile=None, clearLayout=True):
        """
        Prompts the user to create a new profile.
        """
        if profile:
            prof = profile
        elif not self.viewWidget() or clearLayout:
            prof = XViewProfile()
        else:
            prof = self.viewWidget().saveProfile()

        blocked = self.signalsBlocked()
        self.blockSignals(False)
        changed = self.editProfile(prof)
        self.blockSignals(blocked)

        if not changed:
            return

        act = self.addProfile(prof)
        act.setChecked(True)

        # update the interface
        if self.viewWidget() and (profile or clearLayout):
            self.viewWidget().restoreProfile(prof)

        if not self.signalsBlocked():
            self.profileCreated.emit(prof)
            self.profilesChanged.emit()

    @Slot(PyObject)
    def editProfile(self, profile):
        """
        Prompts the user to edit the given profile.
        
        :param      profile | <projexui.widgets.xviewwidget.XViewProfile>
        """
        mod = XViewProfileDialog.edit(self.window(), profile)
        if not mod:
            return False

        # update the action interface
        for act in self._profileGroup.actions():
            if act.profile() == profile:
                act.setProfile(profile)
                break

        # signal the change
        if not self.signalsBlocked():
            self.profileChanged.emit(profile)
            self.profilesChanged.emit()

        return True

    def exportProfile(self, profile, filename=None):
        """
        Exports this toolbar to the given filename.
        
        :param      profile  | <XViewProfile>
                    filename | <str> || None
        """
        if not filename:
            filename = QFileDialog.getSaveFileName(self, 'Export Profile', '',
                                                   'XML Files (*.xml)')
            if type(filename) == tuple:
                filename = nativestring(filename[0])

        if not filename:
            return False

        profile.save(filename)
        return True

    def handleActionTrigger(self, action):
        """
        Handles when an action has been triggered.  If the inputed action is a 
        XViewProfileAction, then the currentProfileChanged signal will emit.
        
        :param      action | <QAction>
        """
        # trigger a particular profile
        if isinstance(action, XViewProfileAction):
            # if the user CTRL+Clicks on the action, then attempt
            # to load it in a new window
            if QApplication.keyboardModifiers() == Qt.ControlModifier:
                self.newWindowProfileRequested.emit(action.profile())
                self.setCurrentProfile(self.currentProfile())
                return
            else:
                self.setCurrentProfile(action.profile())

    def importProfile(self, filename=None):
        """
        Imports the profiles from the given filename.
        
        :param      filename | <str> || None
        """
        if not filename:
            filename = QFileDialog.getOpenFileName(self, 'Import Perspective',
                                                   '', 'XML Files (*.xml)')

            if type(filename) == tuple:
                filename = nativestring(filename[0])

        if not (filename and os.path.exists(filename)):
            return False

        prof = XViewProfile.load(filename)
        if prof:
            self.addProfile(prof)

    def isEditingEnabled(self):
        """
        Sets whether or not the create is enabled for this toolbar.
        
        :return     <bool>
        """
        return self._editingEnabled

    def isEmpty(self):
        """
        Returns whether or not this toolbar is empty.
        
        :return     <bool>
        """
        return len(self._profileGroup.actions()) == 0

    def loadString(self, profilestr, merge=False, loadProfile=True):
        """
        Loads the information for this toolbar from the inputed string.
        
        :param      profilestr | <str>
        """
        try:
            xtoolbar = ElementTree.fromstring(nativestring(profilestr))
        except ExpatError, e:
            return

        if not merge:
            self.clear()

        self.blockSignals(True)
        for xprofile in xtoolbar:
            prof = XViewProfile.fromXml(xprofile)

            if merge:
                self.removeProfile(prof.name(), silent=True)

            self.addProfile(prof)

        self.setCurrentProfile(xtoolbar.get('current'))

        self.blockSignals(False)
        self.profilesChanged.emit()