Reputation: 15
I am working on an application in PyQt5 which is composed of a side menu (on the left) and a content window (on the right):
On the side menu, I have a Settings QPushButton. On click, a new window appears between the two :
I would like to animate the display of this window so that :
Here's my code so far :
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class Settings(QWidget):
def __init__(self):
super().__init__()
self.settingsFrame = QFrame(self)
self.settingsFrame.setMaximumWidth(300)
self.settingsFrame.setObjectName("Settings")
self.settingsLayout = QVBoxLayout(self.settingsFrame)
self.settingsLayout.setContentsMargins(0, 0, 0, 0)
self.settingsLayout.setSpacing(0)
self.settingsLayout.addWidget(QLabel("My settings info 1"))
self.settingsLayout.addWidget(QLabel("My settings info 2"))
self.settingsFrame.hide()
class LeftMenu(QWidget):
def __init__(self, settings_w):
super().__init__(settings_w)
self.settings_w = settings_w
self.leftMenuFrame = QFrame(self)
self.leftMenuFrame.setMaximumWidth(200)
self.leftMenuFrame.setObjectName("LeftMenu")
self.menuLayout = QVBoxLayout(self.leftMenuFrame)
self.menuLayout.setContentsMargins(0, 0, 0, 0)
self.menuLayout.setSpacing(0)
self.menuLayout.addWidget(QLabel("Some informations"))
self.menuLayout.addWidget(QLabel("Some more"))
self.menuLayout.addStretch(1)
self.but = QPushButton("Settings")
self.but.clicked.connect(self.handle_settings)
self.menuLayout.addWidget(self.but)
def handle_settings(self):
if self.settings_w.isHidden():
self.settings_w.settingsFrame.show()
else:
self.settings_w.settingsFrame.hide()
class Window(QWidget):
def __init__(self):
super().__init__()
self.windowFrame = QFrame(self)
self.windowLayout = QGridLayout(self.windowFrame)
self.windowLayout.setContentsMargins(0, 0, 0, 0)
self.windowLayout.setSpacing(0)
self.label = QLabel("CONTENT")
self.settings = Settings()
self.leftMenu = LeftMenu(self.settings)
self.windowLayout.addWidget(self.leftMenu.leftMenuFrame, 0, 0, 1, 1)
self.windowLayout.addWidget(self.settings.settingsFrame, 0, 1, 1, 1)
self.windowLayout.addWidget(self.label, 0, 2, 1, 1, Qt.AlignCenter)
self.setLayout(self.windowLayout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I think I should use QPropertyAnimation but I can't make it work with my example.
Thanks for your help !
EDIT : as suggested by @ekhumoro, Here's what I tried with QPropertyAnimation
if self.settings_w.isHidden():
# self.settings_w.settingsFrame.show()
animation = QPropertyAnimation(self.settings_w.settingsFrame, b"geometry")
animation.setDuration(400)
animation.setStartValue(QRect(0, 0, 0, self.settings_w.settingsFrame.height()))
animation.setEndValue(QRect(0, 0, 300, self.settings_w.settingsFrame.height()))
animation.start()
Upvotes: 1
Views: 124
Reputation: 48399
The main problem with your attempt with the animation is that you created it as a local variable: as soon as handle_settings
returns, the animation gets garbage collected (deleted) and nothing else will happen.
Unfortunately, that won't be enough, as there are other problems.
First of all, your code as some problems from the point of view of object structure: both Settings
and LeftMenu
classes are not really used, as you're actually using the widgets created in their __init__
. Not only this is conceptually wrong, but it also makes your code unnecessarily convoluted and difficult to deal with.
Then there's another issue: showing a previously hidden widget makes its parent layout to instantly resize all other widgets (and nested layout, if any) based on the size of the newly shown widget, and the fact that you are trying to manually set the geometry doesn't change anything, as the layout is not affected by manual geometry changes.
The solution (besides properly structuring the classes) is to use the fixed width of the submenu for the animation, and manually set the geometry of the layout along with it.
Note that this doesn't consider the possibility of a resizable submenu, which would make things much more complex, as the animation should account for the size hints of all other sibling widgets to properly set the end value for the geometry. In this case, I only used the fixed width suggested in the provided example.
class Settings(QWidget):
def __init__(self):
super().__init__()
self.setFixedWidth(0)
self.settingsLayout = QVBoxLayout(self)
self.settingsLayout.setContentsMargins(0, 0, 0, 0)
self.settingsLayout.setSpacing(0)
self.settingsLayout.addWidget(QLabel("My settings info 1"))
self.settingsLayout.addWidget(QLabel("My settings info 2"))
self.animation = QVariantAnimation(self)
self.animation.setDuration(400)
self.animation.setStartValue(0)
self.animation.setEndValue(300)
self.animation.valueChanged.connect(self.updateSize)
def updateSize(self, width):
self.setFixedWidth(width)
self.settingsLayout.setGeometry(QRect(width - 300, 0, 300, self.height()))
def toggle(self):
if self.width() and self.animation.direction() == self.animation.Forward:
self.animation.setDirection(self.animation.Backward)
else:
self.animation.setDirection(self.animation.Forward)
self.animation.start()
class LeftMenu(QFrame):
toggle_settings = pyqtSignal()
def __init__(self):
super().__init__()
self.setObjectName("LeftMenu")
self.menuLayout = QVBoxLayout(self)
self.menuLayout.setContentsMargins(0, 0, 0, 0)
self.menuLayout.setSpacing(0)
self.menuLayout.addWidget(QLabel("Some informations"))
self.menuLayout.addWidget(QLabel("Some more"))
self.menuLayout.addStretch(1)
self.but = QPushButton("Settings")
self.but.clicked.connect(self.toggle_settings)
self.menuLayout.addWidget(self.but)
self.setMaximumWidth(min(200, self.sizeHint().width()))
class Window(QWidget):
def __init__(self):
super().__init__()
self.windowFrame = QFrame(self)
self.windowLayout = QGridLayout(self.windowFrame)
self.windowLayout.setContentsMargins(0, 0, 0, 0)
self.windowLayout.setSpacing(0)
self.label = QLabel("CONTENT")
self.settings_w = Settings()
self.leftMenu = LeftMenu()
self.windowLayout.addWidget(self.leftMenu, 0, 0, 1, 1)
self.windowLayout.addWidget(self.settings_w, 0, 1, 1, 1)
self.windowLayout.addWidget(self.label, 0, 2, 1, 1, Qt.AlignCenter)
self.setLayout(self.windowLayout)
self.leftMenu.toggle_settings.connect(self.settings_w.toggle)
self.setMinimumWidth(self.minimumSizeHint().width() + 300)
Upvotes: 1