source: orange/Orange/OrangeCanvas/gui/dock.py @ 11179:f7ee1338a997

Revision 11179:f7ee1338a997, 7.1 KB checked in by Ales Erjavec <ales.erjavec@…>, 18 months ago (diff)

More code cleanup.

Line 
1"""
2=======================
3Collapsible Dock Widget
4=======================
5
6A dock widget with a header 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
21
22log = logging.getLogger(__name__)
23
24
25class CollapsibleDockWidget(QDockWidget):
26    """A Dock widget for which the close action collapses the widget
27    to a smaller size.
28
29    """
30    def __init__(self, *args, **kwargs):
31        QDockWidget.__init__(self, *args, **kwargs)
32
33        self.__expandedWidget = None
34        self.__collapsedWidget = None
35        self.__expanded = True
36
37        self.setFeatures(QDockWidget.DockWidgetClosable | \
38                         QDockWidget.DockWidgetMovable)
39        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
40
41        self.featuresChanged.connect(self.__onFeaturesChanged)
42        self.dockLocationChanged.connect(self.__onDockLocationChanged)
43
44        # Use the toolbar horizontal extension button icon as the default
45        # for the expand/collapse button
46        pm = self.style().standardPixmap(
47                    QStyle.SP_ToolBarHorizontalExtensionButton
48                )
49
50        # Rotate the icon
51        transform = QTransform()
52        transform.rotate(180)
53
54        pm_rev = pm.transformed(transform)
55
56        self.__iconRight = QIcon(pm)
57        self.__iconLeft = QIcon(pm_rev)
58
59        close = self.findChild(QAbstractButton,
60                               name="qt_dockwidget_closebutton")
61
62        close.installEventFilter(self)
63        self.__closeButton = close
64
65        self.__stack = AnimatedStackedWidget()
66
67        self.__stack.setSizePolicy(QSizePolicy.Fixed,
68                                   QSizePolicy.Expanding)
69
70        self.__stack.transitionStarted.connect(self.__onTransitionStarted)
71        self.__stack.transitionFinished.connect(self.__onTransitionFinished)
72
73        self.__stack.installEventFilter(self)
74
75        QDockWidget.setWidget(self, self.__stack)
76
77        self.__closeButton.setIcon(self.__iconLeft)
78
79    def setExpanded(self, state):
80        """Set the expanded state.
81        """
82        if self.__expanded != state:
83            self.__expanded = state
84            if state and self.__expandedWidget is not None:
85                log.debug("Dock expanding.")
86                self.__stack.setCurrentWidget(self.__expandedWidget)
87            elif not state and self.__collapsedWidget is not None:
88                log.debug("Dock collapsing.")
89                self.__stack.setCurrentWidget(self.__collapsedWidget)
90            self.__fixIcon()
91
92    def expanded(self):
93        """Is the dock widget in expanded state
94        """
95        return self.__expanded
96
97    expanded_ = Property(bool, fset=setExpanded, fget=expanded)
98
99    def setWidget(self, w):
100        raise NotImplementedError(
101                "Please use the setExpandedWidget/setCollapsedWidget method."
102              )
103
104    def setExpandedWidget(self, widget):
105        """Set the widget with contents to show while expanded.
106        """
107        if widget is self.__expandedWidget:
108            return
109
110        if self.__expandedWidget is not None:
111            self.__stack.removeWidget(self.__expandedWidget)
112
113        self.__stack.insertWidget(0, widget)
114        self.__expandedWidget = widget
115
116        if self.__expanded:
117            self.__stack.setCurrentWidget(widget)
118
119    def setCollapsedWidget(self, widget):
120        """Set the widget with contents to show while collapsed.
121        """
122        if widget is self.__collapsedWidget:
123            return
124
125        if self.__collapsedWidget is not None:
126            self.__stack.removeWidget(self.__collapsedWidget)
127
128        self.__stack.insertWidget(1, widget)
129        self.__collapsedWidget = widget
130
131        if not self.__expanded:
132            self.__stack.setCurrentWidget(widget)
133
134    def setAnimationEnabled(self, animationEnabled):
135        """Enable/disable the transition animation.
136        """
137        self.__stack.setAnimationEnabled(animationEnabled)
138
139    def animationEnabled(self):
140        return self.__stack.animationEnabled()
141
142    def currentWidget(self):
143        """Return the current widget.
144        """
145        if self.__expanded:
146            return self.__expandedWidget
147        else:
148            return self.__collapsedWidget
149
150    def _setExpandedState(self, state):
151        """Set the expanded/collapsed state. `True` indicates an
152        expanded state.
153
154        """
155        if state and not self.__expanded:
156            self.expand()
157        elif not state and self.__expanded:
158            self.collapse()
159
160    def expand(self):
161        """Expand the dock (same as `setExpanded(True)`)
162        """
163        self.setExpanded(True)
164
165    def collapse(self):
166        """Collapse the dock (same as `setExpanded(False)`)
167        """
168        self.setExpanded(False)
169
170    def eventFilter(self, obj, event):
171        if obj is self.__closeButton:
172            etype = event.type()
173            if etype == QEvent.MouseButtonPress:
174                self.setExpanded(not self.__expanded)
175                return True
176            elif etype == QEvent.MouseButtonDblClick or \
177                    etype == QEvent.MouseButtonRelease:
178                return True
179            # TODO: which other events can trigger the button (is the button
180            # focusable).
181
182        if obj is self.__stack:
183            etype = event.type()
184            if etype == QEvent.Resize:
185                # If the stack resizes
186                obj.resizeEvent(event)
187                size = event.size()
188                size = self.__stack.sizeHint()
189                if size.width() > 0:
190                    left, _, right, _ = self.getContentsMargins()
191                    self.setFixedWidth(size.width() + left + right)
192                return True
193
194        return QDockWidget.eventFilter(self, obj, event)
195
196    def __onFeaturesChanged(self, features):
197        pass
198
199    def __onDockLocationChanged(self, area):
200        if area == Qt.LeftDockWidgetArea:
201            self.setLayoutDirection(Qt.LeftToRight)
202        else:
203            self.setLayoutDirection(Qt.RightToLeft)
204
205        self.__stack.setLayoutDirection(self.parentWidget().layoutDirection())
206        self.__fixIcon()
207
208    def __onTransitionStarted(self):
209        self.__stack.installEventFilter(self)
210
211    def __onTransitionFinished(self):
212        self.__stack.removeEventFilter(self)
213        size = self.__stack.sizeHint()
214        left, _, right, _ = self.getContentsMargins()
215        self.setFixedWidth(size.width() + left + right)
216        log.debug("Dock transition finished (new width %i)", size.width())
217
218    def __fixIcon(self):
219        """Fix the dock close icon.
220        """
221        direction = self.layoutDirection()
222        if direction == Qt.LeftToRight:
223            if self.__expanded:
224                icon = self.__iconLeft
225            else:
226                icon = self.__iconRight
227        else:
228            if self.__expanded:
229                icon = self.__iconRight
230            else:
231                icon = self.__iconLeft
232
233        self.__closeButton.setIcon(icon)
Note: See TracBrowser for help on using the repository browser.