Одним из возможных решений является реализация Drawer с использованием виджетов Qt, основная функция - анимация изменения ширины, например, с помощью QXAnimation, другая задача - установить привязки так, чтобы они занимали необходимую высоту. Простой пример показан в следующем коде:
import os
from PySide2 import QtCore, QtGui, QtWidgets
class Drawer(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedWidth(0)
self.setContentsMargins(0, 0, 0, 0)
# self.setFixedWidth(0)
self._maximum_width = 0
self._animation = QtCore.QPropertyAnimation(self, b"width")
self._animation.setStartValue(0)
self._animation.setDuration(1000)
self._animation.valueChanged.connect(self.setFixedWidth)
self.hide()
@property
def maximum_width(self):
return self._maximum_width
@maximum_width.setter
def maximum_width(self, w):
self._maximum_width = w
self._animation.setEndValue(self.maximum_width)
def open(self):
self._animation.setDirection(QtCore.QAbstractAnimation.Forward)
self._animation.start()
self.show()
def close(self):
self._animation.setDirection(QtCore.QAbstractAnimation.Backward)
self._animation.start()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
self.tool_button = QtWidgets.QToolButton(
checkable=True, iconSize=QtCore.QSize(36, 36)
)
content_widget = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
content_widget.setText("Content")
content_widget.setStyleSheet("background-color: green")
lay = QtWidgets.QVBoxLayout(central_widget)
lay.setSpacing(0)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(self.tool_button)
lay.addWidget(content_widget)
self.resize(640, 480)
self.drawer = Drawer(self)
self.drawer.move(0, self.tool_button.sizeHint().height())
self.drawer.maximum_width = 200
self.drawer.raise_()
content_lay = QtWidgets.QVBoxLayout()
content_lay.setContentsMargins(0, 0, 0, 0)
label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
label.setText("Content\nDrawer")
label.setStyleSheet("background-color: red")
content_lay.addWidget(label)
self.drawer.setLayout(content_lay)
self.tool_button.toggled.connect(self.onToggled)
self.onToggled(self.tool_button.isChecked())
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.onCustomContextMenuRequested)
@QtCore.Slot()
def onCustomContextMenuRequested(self):
menu = QtWidgets.QMenu()
quit_action = menu.addAction(self.tr("Close"))
action = menu.exec_(QtGui.QCursor.pos())
if action == quit_action:
self.close()
@QtCore.Slot(bool)
def onToggled(self, checked):
if checked:
self.tool_button.setIcon(
self.style().standardIcon(QtWidgets.QStyle.SP_MediaStop)
)
self.drawer.open()
else:
self.tool_button.setIcon(
self.style().standardIcon(QtWidgets.QStyle.SP_MediaPlay)
)
self.drawer.close()
def resizeEvent(self, event):
self.drawer.setFixedHeight(self.height() - self.drawer.pos().y())
super().resizeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())