#------------------------------------------------------------------------------
# Copyright (c) 2012, Enthought, Inc.
# All rights reserved.
#------------------------------------------------------------------------------
import sys
from .qt.QtCore import Qt, Signal
from .qt.QtGui import QToolBar, QMainWindow
from .qt_action import QtAction
from .qt_action_group import QtActionGroup
from .qt_constraints_widget import QtConstraintsWidget
#: A mapping from Enaml dock area to Qt tool bar areas
_DOCK_AREA_MAP = {
'top': Qt.TopToolBarArea,
'right': Qt.RightToolBarArea,
'bottom': Qt.BottomToolBarArea,
'left': Qt.LeftToolBarArea,
'all': Qt.AllToolBarAreas,
}
#: A mapping from Qt tool bar areas to Enaml dock areas
_DOCK_AREA_INV_MAP = {
Qt.TopToolBarArea: 'top',
Qt.RightToolBarArea: 'right',
Qt.BottomToolBarArea: 'bottom',
Qt.LeftToolBarArea: 'left',
Qt.AllToolBarAreas: 'all',
}
#: A mapping from Enaml orientation to Qt Orientation
_ORIENTATION_MAP = {
'horizontal': Qt.Horizontal,
'vertical': Qt.Vertical,
}
class QCustomToolBar(QToolBar):
""" A custom QToolBar which adds some Enaml specific features.
"""
#: A signal emitted when the dock widget is floated.
floated = Signal()
#: A signal emitted when the dock widget is docked. The payload
#: will be the new dock area.
docked = Signal(object)
def __init__(self, *args, **kwargs):
""" Initialize a QCustomToolBar.
Parameters
----------
*args, **kwargs
The positional and keyword arguments needed to initialize
a QToolBar.
"""
super(QCustomToolBar, self).__init__(*args, **kwargs)
self._tool_bar_area = Qt.TopToolBarArea
self.topLevelChanged.connect(self._onTopLevelChanged)
#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _onTopLevelChanged(self, top_level):
""" The signal handler for the the 'topLevelChanged' signal.
"""
if top_level:
self.floated.emit()
else:
parent = self.parent()
if parent is not None and isinstance(parent, QMainWindow):
self._tool_bar_area = parent.toolBarArea(self)
self.docked.emit(self._tool_bar_area)
#--------------------------------------------------------------------------
# Public API
#--------------------------------------------------------------------------
def removeActions(self, actions):
""" Remove the given actions from the tool bar.
Parameters
----------
actions : iterable
An iterable of QActions to remove from the tool bar.
"""
remove = self.removeAction
for action in actions:
remove(action)
def toolBarArea(self):
""" Get the current tool bar area for the tool bar.
Returns
-------
result : QToolBarArea
The tool bar area where this tool bar resides.
"""
return self._tool_bar_area
def setToolBarArea(self, area):
""" Set the current tool bar area for the tool bar.
Parameters
----------
area : QToolBarArea
The tool bar area where this tool bar should reside.
"""
self._tool_bar_area = area
parent = self.parent()
if isinstance(parent, QMainWindow):
parent.setToolBarArea(area, self)
def setFloating(self, floating):
""" Set the floating state of the tool bar.
Parameters
----------
floating : bool
Whether or not the tool bar should floating.
"""
# QToolBar doesn't provide a setFloating() method. This code
# is taken mostly from QToolBarPrivate::updateWindowFlags.
parent = self.parent()
if isinstance(parent, QMainWindow):
visible = self.isVisibleTo(parent)
flags = Qt.Tool if floating else Qt.Widget
flags |= Qt.FramelessWindowHint
if sys.platform == 'darwin':
flags |= Qt.WindowStaysOnTopHint
self.setWindowFlags(flags)
if visible:
self.resize(self.sizeHint())
self.setVisible(True)
[docs]class QtToolBar(QtConstraintsWidget):
""" A Qt implementation of an Enaml ToolBar.
"""
#--------------------------------------------------------------------------
# Setup Methods
#--------------------------------------------------------------------------
[docs] def create_widget(self, parent, tree):
""" Create the underlying tool bar widget.
"""
return QCustomToolBar(parent)
[docs] def create(self, tree):
""" Create and initialize the underlying tool bar control.
"""
super(QtToolBar, self).create(tree)
self.set_movable(tree['movable'])
self.set_floatable(tree['floatable'])
self.set_floating(tree['floating'])
self.set_dock_area(tree['dock_area'])
self.set_allowed_dock_areas(tree['allowed_dock_areas'])
self.set_orientation(tree['orientation'])
widget = self.widget()
widget.floated.connect(self.on_floated)
widget.docked.connect(self.on_docked)
[docs] def init_layout(self):
""" Initialize the layout for the toolbar.
"""
super(QtToolBar, self).init_layout()
widget = self.widget()
for child in self.children():
if isinstance(child, QtAction):
widget.addAction(child.widget())
elif isinstance(child, QtActionGroup):
widget.addActions(child.actions())
#--------------------------------------------------------------------------
# Child Events
#--------------------------------------------------------------------------
[docs] def child_removed(self, child):
""" Handle the child removed event for a QtToolBar.
"""
if isinstance(child, QtAction):
self.widget().removeAction(child.widget())
elif isinstance(child, QtActionGroup):
self.widget().removeActions(child.actions())
[docs] def child_added(self, child):
""" Handle the child added event for a QtToolBar.
"""
before = self.find_next_action(child)
if isinstance(child, QtAction):
self.widget().insertAction(before, child.widget())
elif isinstance(child, QtActionGroup):
self.widget().insertActions(before, child.actions())
#--------------------------------------------------------------------------
# Utility Methods
#--------------------------------------------------------------------------
[docs] def find_next_action(self, child):
""" Get the QAction instance which comes immediately after the
actions of the given child.
Parameters
----------
child : QtActionGroup, or QtAction
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.
"""
# The target action must be tested for membership against the
# current actions on the tool bar itself, since this method may
# be called after a child is added, but before the actions for
# the child have actually been added to the tool bar.
index = self.index_of(child)
if index != -1:
actions = set(self.widget().actions())
for child in self.children()[index + 1:]:
target = None
if isinstance(child, QtAction):
target = child.widget()
elif isinstance(child, QtActionGroup):
acts = child.actions()
target = acts[0] if acts else None
if target in actions:
return target
#--------------------------------------------------------------------------
# Signal Handlers
#--------------------------------------------------------------------------
[docs] def on_floated(self):
""" The signal handler for the 'floated' signal.
"""
if 'floating' not in self.loopback_guard:
self.send_action('floated', {})
[docs] def on_docked(self, area):
""" The signal handler for the 'docked' signal.
"""
if 'floating' not in self.loopback_guard:
content = {'dock_area': _DOCK_AREA_INV_MAP[area]}
self.send_action('docked', content)
#--------------------------------------------------------------------------
# Message Handling
#--------------------------------------------------------------------------
[docs] def on_action_set_movable(self, content):
""" Handle the 'set_movable' action from the Enaml widget.
"""
self.set_movable(content['movable'])
[docs] def on_action_set_floatable(self, content):
""" Handle the 'set_floatable' action from the Enaml widget.
"""
self.set_floatable(content['floatable'])
[docs] def on_action_set_floating(self, content):
""" Handle the 'set_floating' action from the Enaml widget.
"""
self.set_floating(content['floating'])
[docs] def on_action_set_dock_area(self, content):
""" Handle the 'set_dock_area' action from the Enaml widget.
"""
self.set_dock_area(content['dock_area'])
[docs] def on_action_set_allowed_dock_areas(self, content):
""" Handle the 'set_allowed_dock_areas' action from the Enaml
widget.
"""
self.set_allowed_dock_areas(content['allowed_dock_areas'])
[docs] def on_action_set_orientation(self, content):
""" Handle the 'set_orientation' action from the Enaml widget.
"""
self.set_orientation(content['orientation'])
#--------------------------------------------------------------------------
# Widget Update Methods
#--------------------------------------------------------------------------
[docs] def set_movable(self, movable):
""" Set the movable state on the underlying widget.
"""
self.widget().setMovable(movable)
[docs] def set_floatable(self, floatable):
""" Set the floatable state on the underlying widget.
"""
self.widget().setFloatable(floatable)
[docs] def set_floating(self, floating):
""" Set the floating staet on the underlying widget.
"""
with self.loopback_guard('floating'):
self.widget().setFloating(floating)
[docs] def set_dock_area(self, dock_area):
""" Set the dock area on the underyling widget.
"""
self.widget().setToolBarArea(_DOCK_AREA_MAP[dock_area])
[docs] def set_allowed_dock_areas(self, dock_areas):
""" Set the allowed dock areas on the underlying widget.
"""
qt_areas = Qt.NoToolBarArea
for area in dock_areas:
qt_areas |= _DOCK_AREA_MAP[area]
self.widget().setAllowedAreas(qt_areas)
[docs] def set_orientation(self, orientation):
""" Set the orientation of the underlying widget.
"""
# If the tool bar is a child of a QMainWindow, then that window
# will take control of setting its orientation and changes to
# the orientation by the user must be ignored.
widget = self.widget()
parent = widget.parent()
if not isinstance(parent, QMainWindow):
widget.setOrientation(_ORIENTATION_MAP[orientation])