source: orange/Orange/OrangeCanvas/gui/dock.py @ 11366:7f9332b11252

Revision 11366:7f9332b11252, 8.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 14 months ago (diff)

Added rst documentation for the canvas gui package.

Fixing docstrings in the process.

Line 
1"""
2=======================
3Collapsible Dock Widget
4=======================
5
6A dock widget that can be a collapsed/expanded.
7
8"""
9
10import logging
11
12from PyQt4.QtGui import (
13    QDockWidget, QAbstractButton, QSizePolicy, QStyle, QIcon, QTransform
14)
15
16from PyQt4.QtCore import Qt, QEvent
17
18from PyQt4.QtCore import pyqtProperty as Property
19
20from .stackedwidget import AnimatedStackedWidget
21from .utils import QWIDGETSIZE_MAX
22
23log = logging.getLogger(__name__)
24
25
26class CollapsibleDockWidget(QDockWidget):
27    """
28    This :class:`QDockWidget` subclass overrides the `close` header
29    button to instead collapse to a smaller size. The contents contents
30    to show when in each state can be set using the ``setExpandedWidget``
31    and ``setCollapsedWidget``.
32
33    .. note:: Do use the base class ``QDockWidget.setWidget`` method to set
34              the contents.
35
36    """
37    def __init__(self, *args, **kwargs):
38        QDockWidget.__init__(self, *args, **kwargs)
39
40        self.__expandedWidget = None
41        self.__collapsedWidget = None
42        self.__expanded = True
43
44        self.__trueMinimumWidth = -1
45
46        self.setFeatures(QDockWidget.DockWidgetClosable | \
47                         QDockWidget.DockWidgetMovable)
48        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
49
50        self.featuresChanged.connect(self.__onFeaturesChanged)
51        self.dockLocationChanged.connect(self.__onDockLocationChanged)
52
53        # Use the toolbar horizontal extension button icon as the default
54        # for the expand/collapse button
55        pm = self.style().standardPixmap(
56            QStyle.SP_ToolBarHorizontalExtensionButton
57        )
58
59        # Rotate the icon
60        transform = QTransform()
61        transform.rotate(180)
62
63        pm_rev = pm.transformed(transform)
64
65        self.__iconRight = QIcon(pm)
66        self.__iconLeft = QIcon(pm_rev)
67
68        close = self.findChild(QAbstractButton,
69                               name="qt_dockwidget_closebutton")
70
71        close.installEventFilter(self)
72        self.__closeButton = close
73
74        self.__stack = AnimatedStackedWidget()
75
76        self.__stack.setSizePolicy(QSizePolicy.Fixed,
77                                   QSizePolicy.Expanding)
78
79        self.__stack.transitionStarted.connect(self.__onTransitionStarted)
80        self.__stack.transitionFinished.connect(self.__onTransitionFinished)
81
82        QDockWidget.setWidget(self, self.__stack)
83
84        self.__closeButton.setIcon(self.__iconLeft)
85
86    def setExpanded(self, state):
87        """
88        Set the widgets `expanded` state.
89        """
90        if self.__expanded != state:
91            self.__expanded = state
92            if state and self.__expandedWidget is not None:
93                log.debug("Dock expanding.")
94                self.__stack.setCurrentWidget(self.__expandedWidget)
95            elif not state and self.__collapsedWidget is not None:
96                log.debug("Dock collapsing.")
97                self.__stack.setCurrentWidget(self.__collapsedWidget)
98            self.__fixIcon()
99
100    def expanded(self):
101        """
102        Is the dock widget in expanded state. When `True` the
103        ``expandedWidget`` will be shown, and ``collapsedWidget`` otherwise.
104
105        """
106        return self.__expanded
107
108    expanded_ = Property(bool, fset=setExpanded, fget=expanded)
109
110    def setWidget(self, w):
111        raise NotImplementedError(
112                "Please use the 'setExpandedWidget'/'setCollapsedWidget' "
113                "methods to set the contents of the dock widget."
114              )
115
116    def setExpandedWidget(self, widget):
117        """
118        Set the widget with contents to show while expanded.
119        """
120        if widget is self.__expandedWidget:
121            return
122
123        if self.__expandedWidget is not None:
124            self.__stack.removeWidget(self.__expandedWidget)
125
126        self.__stack.insertWidget(0, widget)
127        self.__expandedWidget = widget
128
129        if self.__expanded:
130            self.__stack.setCurrentWidget(widget)
131            self.updateGeometry()
132
133    def expandedWidet(self):
134        """
135        Return the widget previously set with ``setExpandedWidget``,
136        or ``None`` if no widget has been set.
137
138        """
139        return self.__expandedWidget
140
141    def setCollapsedWidget(self, widget):
142        """
143        Set the widget with contents to show while collapsed.
144        """
145        if widget is self.__collapsedWidget:
146            return
147
148        if self.__collapsedWidget is not None:
149            self.__stack.removeWidget(self.__collapsedWidget)
150
151        self.__stack.insertWidget(1, widget)
152        self.__collapsedWidget = widget
153
154        if not self.__expanded:
155            self.__stack.setCurrentWidget(widget)
156            self.updateGeometry()
157
158    def collapsedWidget(self):
159        """
160        Return the widget previously set with ``setCollapsedWidget``,
161        or ``None`` if no widget has been set.
162
163        """
164        return self.__collapsedWidget
165
166    def setAnimationEnabled(self, animationEnabled):
167        """
168        Enable/disable the transition animation.
169        """
170        self.__stack.setAnimationEnabled(animationEnabled)
171
172    def animationEnabled(self):
173        """
174        Is transition animation enabled.
175        """
176        return self.__stack.animationEnabled()
177
178    def currentWidget(self):
179        """
180        Return the current shown widget depending on the `expanded` state.
181        """
182        if self.__expanded:
183            return self.__expandedWidget
184        else:
185            return self.__collapsedWidget
186
187    def expand(self):
188        """
189        Expand the dock (same as ``setExpanded(True)``)
190        """
191        self.setExpanded(True)
192
193    def collapse(self):
194        """
195        Collapse the dock (same as ``setExpanded(False)``)
196        """
197        self.setExpanded(False)
198
199    def eventFilter(self, obj, event):
200        if obj is self.__closeButton:
201            etype = event.type()
202            if etype == QEvent.MouseButtonPress:
203                self.setExpanded(not self.__expanded)
204                return True
205            elif etype == QEvent.MouseButtonDblClick or \
206                    etype == QEvent.MouseButtonRelease:
207                return True
208            # TODO: which other events can trigger the button (is the button
209            # focusable).
210
211        return QDockWidget.eventFilter(self, obj, event)
212
213    def event(self, event):
214        if event.type() == QEvent.LayoutRequest:
215            self.__fixMinimumWidth()
216
217        return QDockWidget.event(self, event)
218
219    def __onFeaturesChanged(self, features):
220        pass
221
222    def __onDockLocationChanged(self, area):
223        if area == Qt.LeftDockWidgetArea:
224            self.setLayoutDirection(Qt.LeftToRight)
225        else:
226            self.setLayoutDirection(Qt.RightToLeft)
227
228        self.__stack.setLayoutDirection(self.parentWidget().layoutDirection())
229        self.__fixIcon()
230
231    def __onTransitionStarted(self):
232        log.debug("Dock transition started.")
233
234    def __onTransitionFinished(self):
235        log.debug("Dock transition finished (new width %i)",
236                  self.size().width())
237
238    def __fixMinimumWidth(self):
239        # A workaround for forcing the QDockWidget layout to disregard the
240        # default minimumSize which can be to wide for us (overriding the
241        # minimumSizeHint or setting the minimum size directly does not
242        # seem to have an effect (Qt 4.8.3).
243        size = self.__stack.sizeHint()
244        if size.isValid() and not size.isEmpty():
245            left, _, right, _ = self.getContentsMargins()
246            width = size.width() + left + right
247
248            if width < self.minimumSizeHint().width():
249                if not self.__hasFixedWidth():
250                    log.debug("Overriding default minimum size "
251                              "(setFixedWidth(%i))", width)
252                    self.__trueMinimumWidth = self.minimumSizeHint().width()
253                self.setFixedWidth(width)
254            else:
255                if self.__hasFixedWidth():
256                    if width >= self.__trueMinimumWidth:
257                        # Unset the fixed size.
258                        log.debug("Restoring default minimum size "
259                                  "(setFixedWidth(%i))", QWIDGETSIZE_MAX)
260                        self.__trueMinimumWidth = -1
261                        self.setFixedWidth(QWIDGETSIZE_MAX)
262                        self.updateGeometry()
263                    else:
264                        self.setFixedWidth(width)
265
266    def __hasFixedWidth(self):
267        return self.__trueMinimumWidth >= 0
268
269    def __fixIcon(self):
270        """Fix the dock close icon.
271        """
272        direction = self.layoutDirection()
273        if direction == Qt.LeftToRight:
274            if self.__expanded:
275                icon = self.__iconLeft
276            else:
277                icon = self.__iconRight
278        else:
279            if self.__expanded:
280                icon = self.__iconRight
281            else:
282                icon = self.__iconLeft
283
284        self.__closeButton.setIcon(icon)
Note: See TracBrowser for help on using the repository browser.