Krita Source Code Documentation
Loading...
Searching...
No Matches
dockwidget.py
Go to the documentation of this file.
1# Ported from KoDockWidgetTitleBar.cpp which is part of KOffice
2# SPDX-FileCopyrightText: 2007 Marijn Kruisselbrink <m.kruisselbrink@student.tue.nl>
3# SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
4# SPDX-License-Identifier: GPL-2.0-or-later
5import os
6
7try:
8 from PyQt6.QtCore import QPoint, QSize, Qt, QRect, QTimer
9 from PyQt6.QtGui import (QIcon, QPainter)
10 from PyQt6.QtWidgets import (QAbstractButton, QApplication, QComboBox,
11 QDockWidget, QHBoxLayout, QLayout, QMainWindow,
12 QPushButton, QStyle, QStyleOptionDockWidget,
13 QStyleOptionToolButton, QStylePainter, QWidget)
14except:
15 from PyQt5.QtCore import QPoint, QSize, Qt, QRect, QTimer
16 from PyQt5.QtGui import (QIcon, QPainter)
17 from PyQt5.QtWidgets import (QAbstractButton, QApplication, QComboBox,
18 QDockWidget, QHBoxLayout, QLayout, QMainWindow,
19 QPushButton, QStyle, QStyleOptionDockWidget,
20 QStyleOptionToolButton, QStylePainter, QWidget)
21
22
23import dockwidget_icons
24
25
26def hasFeature(dockwidget, feature):
27 return dockwidget.features() & feature == feature
28
29
30class DockWidgetTitleBarButton(QAbstractButton):
31
32 def __init__(self, titlebar):
33 QAbstractButton.__init__(self, titlebar)
34 self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
35
36 def sizeHint(self):
37 self.ensurePolished()
38 margin = self.style().pixelMetric(QStyle.PixelMetric.PM_DockWidgetTitleBarButtonMargin, None, self)
39 if self.icon().isNull():
40 return QSize(margin, margin)
41 iconSize = self.style().pixelMetric(QStyle.PixelMetric.PM_SmallIconSize, None, self)
42 pm = self.icon().pixmap(iconSize)
43 return QSize(pm.width() + margin, pm.height() + margin)
44
45 def enterEvent(self, event):
46 if self.isEnabled():
47 self.update()
48 QAbstractButton.enterEvent(self, event)
49
50 def leaveEvent(self, event):
51 if self.isEnabled():
52 self.update()
53 QAbstractButton.leaveEvent(self, event)
54
55 def paintEvent(self, event):
56 p = QPainter(self)
57 r = self.rect()
58 opt = QStyleOptionToolButton()
59 opt.init(self)
60 opt.state |= QStyle.StateFlag.State_AutoRaise
61 if self.isEnabled() and self.underMouse() and \
62 not self.isChecked() and not self.isDown():
63 opt.state |= QStyle.StateFlag.State_Raised
64 if self.isChecked():
65 opt.state |= QStyle.StateFlag.State_On
66 if self.isDown():
67 opt.state |= QStyle.StateFlag.State_Sunken
68 self.style().drawPrimitive(
69 QStyle.PrimitiveElement.PE_PanelButtonTool, opt, p, self)
70 opt.icon = self.icon()
71 opt.subControls = QStyle.SubControls()
72 opt.activeSubControls = QStyle.SubControls()
73 opt.features = QStyleOptionToolButton.ToolButtonFeature.None_
74 opt.arrowType = Qt.ArrowType.NoArrow
75 size = self.style().pixelMetric(QStyle.PixelMetric.PM_SmallIconSize, None, self)
76 opt.iconSize = QSize(size, size)
77 self.style().drawComplexControl(QStyle.ComplexControl.CC_ToolButton, opt, p, self)
78
79
80class DockWidgetTitleBar(QWidget):
81 # XXX: support QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar feature
82
83 def __init__(self, dockWidget):
84 QWidget.__init__(self, dockWidget)
85 self.openIcon = QIcon(":arrow-down.png")
86 self.closeIcon = QIcon(":arrow-right.png")
87 self.pinIcon = QIcon(":pin.png")
88 q = dockWidget
90 self.floatButton.setIcon(q.style().standardIcon(
91 QStyle.StandardPixmap.SP_TitleBarNormalButton, None, q))
92 self.floatButton.clicked.connect(self.toggleFloatingtoggleFloating)
93 self.floatButton.setVisible(True)
95 self.closeButton.setIcon(q.style().standardIcon(
96 QStyle.StandardPixmap.SP_TitleBarCloseButton, None, q))
97 self.closeButton.clicked.connect(dockWidget.close)
98 self.closeButton.setVisible(True)
100 self.collapseButton.setIcon(self.openIcon)
102 self.collapseButton.setVisible(True)
104 self.pinButton.setIcon(self.pinIcon)
105 self.pinButton.setCheckable(True)
106 self.pinButton.setChecked(True)
107 self.pinButton.clicked.connect(self.togglePinnedtogglePinned)
108 self.pinButton.setVisible(True)
109 dockWidget.featuresChanged.connect(self.featuresChangedfeaturesChanged)
111
113 return self.sizeHint()
114
115 def sizeHint(self):
116 q = self.parentWidget()
117 mw = q.style().pixelMetric(QStyle.PixelMetric.PM_DockWidgetTitleMargin, None, q)
118 fw = q.style().pixelMetric(QStyle.PixelMetric.PM_DockWidgetFrameWidth, None, q)
119 closeSize = QSize(0, 0)
120 if self.closeButton:
121 closeSize = self.closeButton.sizeHint()
122 floatSize = QSize(0, 0)
123 if self.floatButton:
124 floatSize = self.floatButton.sizeHint()
125 hideSize = QSize(0, 0)
126 if self.collapseButton:
127 hideSize = self.collapseButton.sizeHint()
128 pinSize = QSize(0, 0)
129 if self.pinButton:
130 pinSize = self.pinButton.sizeHint()
131 buttonHeight = max(max(closeSize.height(), floatSize.height()),
132 hideSize.height(), pinSize.height()) + 2
133 buttonWidth = closeSize.width() + floatSize.width() + hideSize.width() + pinSize.width()
134 titleFontMetrics = q.fontMetrics()
135 fontHeight = titleFontMetrics.lineSpacing() + 2 * mw
136 height = max(buttonHeight, fontHeight)
137 width = buttonWidth + height + 4 * mw + 2 * fw
138 if hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar):
139 width, height = height, width
140 return QSize(width, height)
141
142 def paintEvent(self, event):
143 p = QStylePainter(self)
144 q = self.parentWidget()
145 if hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar):
146 fw = 1 or q.isFloating() and q.style().pixelMetric(
147 QStyle.PM_DockWidgetFrameWidth, None, q) or 0
148 mw = q.style().pixelMetric(QStyle.PM_DockWidgetTitleMargin, None, q)
149 titleOpt = QStyleOptionDockWidget()
150 titleOpt.initFrom(q)
151 titleOpt.verticalTitleBar = True
152 titleOpt.rect = QRect(
153 QPoint(fw, fw + mw +
154 self.collapseButton.size().height() + self.pinButton.size().height()),
155 QSize(
156 self.geometry().width() - (fw * 2),
157 self.geometry().height() - (fw * 2) -
158 mw - self.collapseButton.size().height() - self.pinButton.size().height()))
159 titleOpt.title = q.windowTitle()
160 titleOpt.closable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetClosable)
161 titleOpt.floatable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetFloatable)
162 p.drawControl(QStyle.CE_DockWidgetTitle, titleOpt)
163 else:
164 fw = q.isFloating() and q.style().pixelMetric(
165 QStyle.PM_DockWidgetFrameWidth, None, q) or 0
166 mw = q.style().pixelMetric(QStyle.PixelMetric.PM_DockWidgetTitleMargin, None, q)
167 titleOpt = QStyleOptionDockWidget()
168 titleOpt.initFrom(q)
169 titleOpt.rect = QRect(
170 QPoint(fw + mw +
171 self.collapseButton.size().width() + self.pinButton.size().width(), fw),
172 QSize(
173 self.geometry().width() - (fw * 2) -
174 mw - self.collapseButton.size().width() - self.pinButton.size().width(),
175 self.geometry().height() - (fw * 2)))
176 titleOpt.title = q.windowTitle()
177 titleOpt.closable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetClosable)
178 titleOpt.floatable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetFloatable)
179 p.drawControl(QStyle.ControlElement.CE_DockWidgetTitle, titleOpt)
180
181 def resizeEvent(self, event):
182 q = self.parentWidget()
183 if hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar):
184 fh = q.isFloating() and q.style().pixelMetric(
185 QStyle.PixelMetric.PM_DockWidgetFrameWidth, None, q) or 0
186 opt = QStyleOptionDockWidget()
187 opt.initFrom(q)
188 opt.verticalTitleBar = True
189 opt.rect = QRect(
190 QPoint(fh, 40), # self.geometry().height() - (fh * 3)),
191 QSize(
192 self.geometry().width() - (fh * 2),
193 fh * 2))
194 opt.title = q.windowTitle()
195 opt.closable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetClosable)
196 opt.floatable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetFloatable)
197 floatRect = q.style().subElementRect(
198 QStyle.SubElement.SubElement.SE_DockWidgetFloatButton, opt, q)
199 if not floatRect.isNull():
200 self.floatButton.setGeometry(floatRect)
201 closeRect = q.style().subElementRect(
202 QStyle.SubElement.SubElement.SE_DockWidgetCloseButton, opt, q)
203 if not closeRect.isNull():
204 self.closeButton.setGeometry(closeRect)
205 top = fh
206 if not floatRect.isNull():
207 top = floatRect.x()
208 elif not closeRect.isNull():
209 top = closeRect.x()
210 size = self.collapseButton.size()
211 if not closeRect.isNull():
212 size = self.closeButton.size()
213 elif not floatRect.isNull():
214 size = self.floatButton.size()
215 collapseRect = QRect(QPoint(top, fh), size)
216 self.collapseButton.setGeometry(collapseRect)
217 pinRect = QRect(QPoint(top, fh + collapseRect.height() + 1), size)
218 self.pinButton.setGeometry(pinRect)
219 else:
220 fw = q.isFloating() and q.style().pixelMetric(
221 QStyle.PixelMetric.PixelMetric.PM_DockWidgetFrameWidth, None, q) or 0
222 opt = QStyleOptionDockWidget()
223 opt.initFrom(q)
224 opt.rect = QRect(
225 QPoint(fw, fw),
226 QSize(
227 self.geometry().width() - (fw * 2),
228 self.geometry().height() - (fw * 2)))
229 opt.title = q.windowTitle()
230 opt.closable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetFeature.DockWidgetClosable)
231 opt.floatable = hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetFeature.DockWidgetFloatable)
232 floatRect = q.style().subElementRect(
233 QStyle.SubElement.SE_DockWidgetFloatButton, opt, q)
234 if not floatRect.isNull():
235 self.floatButton.setGeometry(floatRect)
236 closeRect = q.style().subElementRect(
237 QStyle.SubElement.SE_DockWidgetCloseButton, opt, q)
238 if not closeRect.isNull():
239 self.closeButton.setGeometry(closeRect)
240 top = fw
241 if not floatRect.isNull():
242 top = floatRect.y()
243 elif not closeRect.isNull():
244 top = closeRect.y()
245 size = self.collapseButton.size()
246 if not closeRect.isNull():
247 size = self.closeButton.size()
248 elif not floatRect.isNull():
249 size = self.floatButton.size()
250 collapseRect = QRect(QPoint(fw, top), size)
251 self.collapseButton.setGeometry(collapseRect)
252 pinRect = QRect(QPoint(fw + collapseRect.width() + 1, top), size)
253 self.pinButton.setGeometry(pinRect)
254
255 def setCollapsed(self, collapsed):
256 q = self.parentWidget()
257 if q and q.widget() and q.widget().isHidden() != collapsed:
259
260 def toggleFloating(self):
261 q = self.parentWidget()
262 q.setFloating(not q.isFloating())
263
265 q = self.parentWidget()
266 if not q:
267 return
268 q.toggleCollapsed()
269 self.setCollapsedIcon(q.isCollapsed())
270
271 def setCollapsedIcon(self, flag):
272 self.collapseButton.setIcon(flag and self.openIcon or self.closeIcon)
273
274 def togglePinned(self, checked):
275 self.parent().setPinned(checked)
276
277 def featuresChanged(self, features):
278 q = self.parentWidget()
279 self.closeButton.setVisible(hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetClosable))
280 self.floatButton.setVisible(hasFeature(q, QDockWidget.DockWidgetFeature.DockWidgetFloatable))
281 # self.resizeEvent(None)
282
283
285
286 def __init__(self, dockwidget):
287 QWidget.__init__(self, dockwidget)
288 self.widget = None
289 self.hlayout = QHBoxLayout(self)
290 self.hlayout.setSpacing(0)
291 self.hlayout.setContentsMargins(0, 0, 0, 0)
292 self.setLayout(self.hlayout)
293
294 def setWidget(self, widget):
295 self.widget = widget
296 self.widget_height = widget.height
297 self.layout().addWidget(widget)
298
299 def isCollapsed(self):
300 return self.widget.isVisible()
301
302 def setCollapsed(self, flag):
303 if not flag:
304 self.old_size = self.size()
305 self.layout().removeWidget(self.widget)
306 self.widget.hide()
307 if hasFeature(self.parent(), QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar):
308 self.parent().setMaximumWidth(self.parent().width() - self.width())
309 else:
310 self.parent().setMaximumHeight(self.parent().height() - self.height())
311 else:
312 self.setFixedSize(self.old_size)
313 self.parent().setMinimumSize(QSize(1, 1))
314 self.parent().setMaximumSize(QSize(32768, 32768))
315 self.widget.show()
316 self.layout().addWidget(self.widget)
317 self.setMinimumSize(QSize(1, 1))
318 self.setMaximumSize(QSize(32768, 32768))
319
320
321class DockWidget(QDockWidget):
322
323 def __init__(self, *args):
324 QDockWidget.__init__(self, *args)
326 self.setTitleBarWidget(self.titleBar)
327 self.mainWidget = None
328 self.entered = False
329 self.pinned = True
330 self.shot = False
331
332 def enterEvent(self, event):
333 self.entered = True
334 if not self.shot and not self.isPinned() and not self.isFloating():
335 self.shot = True
336 QTimer.singleShot(500, self.autoshowautoshow)
337 return QDockWidget.enterEvent(self, event)
338
339 def leaveEvent(self, event):
340 self.entered = False
341 if not self.shot and not self.isPinned() and not self.isFloating():
342 self.shot = True
343 QTimer.singleShot(1000, self.autohideautohide)
344 return QDockWidget.leaveEvent(self, event)
345
346 def autohide(self):
347 self.shot = False
348 if not self.entered:
349 self.setCollapsed(False)
350
351 def autoshow(self):
352 self.shot = False
353 if self.entered:
354 self.setCollapsed(True)
355
356 def isPinned(self):
357 return self.pinned
358
359 def setPinned(self, flag):
360 self.pinned = flag
361
362 def setWidget(self, widget):
364 self.mainWidget.setWidget(widget)
365 QDockWidget.setWidget(self, self.mainWidget)
366
367 def setCollapsed(self, flag):
368 self.mainWidget.setCollapsed(flag)
369 self.titleBarWidget().setCollapsedIcon(flag)
370
371 def isCollapsed(self):
372 return self.mainWidget.isCollapsed()
373
375 self.setCollapsed(not self.isCollapsed())
376
377
378if __name__ == "__main__":
379 import sys
380 from PyQt5.QtGui import QTextEdit
381 app = QApplication(sys.argv)
382 app.setStyle("qtcurve")
383 win = QMainWindow()
384 dock1 = DockWidget("1st dockwidget", win)
385 dock1.setFeatures(dock1.features() | QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar)
386 combo = QComboBox(dock1)
387 dock1.setWidget(combo)
388 win.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock1)
389 dock2 = DockWidget("2nd dockwidget")
390 dock2.setFeatures(dock1.features() | QDockWidget.DockWidgetFeature.DockWidgetVerticalTitleBar)
391 button = QPushButton("Hello, world!", dock2)
392 dock2.setWidget(button)
393 win.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, dock2)
394 edit = QTextEdit(win)
395 win.setCentralWidget(edit)
396 win.resize(640, 480)
397 win.show()
398 app.exec()
hasFeature(dockwidget, feature)
Definition dockwidget.py:26