Как я могу изменить размер QSCrollBar, чтобы изменить его ширину во время выполнения?

В настоящее время я пытаюсь воспроизвести полосу прокрутки меню «Пуск» Window 10 в Qt (python), но я не могу понять, как изменить размер моего пользовательского QScrollBar, чтобы изменить его ширину во время выполнения.

Я попытался изменить его размер с помощью метода QScrollBar.resize (в enterEvent и leaveEvent), но он масштабирует виджет за пределами области рисования. Например, моя полоса прокрутки настроена на QScrollArea, и когда я пытаюсь изменить ее размер, она не занимает больше места и не перемещает виджеты, вместо этого она просто масштабируется справа, где я ее не вижу.

Единственное решение, которое я нашел на данный момент, - это использовать StyleSheet, но я не могу анимировать его, чтобы получить плавное изменение размера, которое я ищу.

Есть код, который вы можете протестировать и посмотреть, что не так:

from PySide2 import QtWidgets, QtCore
from functools import partial

class MyTE(QtWidgets.QPlainTextEdit):
    
    def __init__(self):
        super(MyTE, self).__init__()
        
        self.setVerticalScrollBar(MyScrollBar(self))
        self.setPlainText('mmmmmmmmmmmmmmmmmmmmmmmmmmmmm'*50)
        
class MyScrollBar(QtWidgets.QScrollBar):
    
    def __init__(self, parent=None):
        super(MyScrollBar, self).__init__(parent=parent)
        self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)        
        
    def enterEvent(self, event):
        super(MyScrollBar, self).enterEvent(event)
        
        self.resize(QtCore.QSize(4, self.height()))
        
    def leaveEvent(self, event):
        super(MyScrollBar, self).leaveEvent(event)
        
        self.resize(QtCore.QSize(10, self.height()))
        
wid = MyTE()
wid.show()

person Sneyek    schedule 22.10.2020    source источник
comment
предоставьте минимальный воспроизводимый пример   -  person eyllanesc    schedule 22.10.2020
comment
Сделанный ! Могу я спросить, почему вы удалили последнюю строку моего сообщения? Я просто хотел сказать спасибо за помощь..   -  person Sneyek    schedule 22.10.2020
comment
Именно по этой причине. Приветствия не имеют значения, так как сообщение должно быть сосредоточено на проблеме, поэтому избегайте приветствий, благодарностей, информации о вашем уровне знаний или информации, которая не имеет отношения к делу. Сообщество понимает, что каждый участник благодарен любому члену сообщества, поэтому нет необходимости указывать это явно.   -  person eyllanesc    schedule 22.10.2020


Ответы (2)


Чтобы сделать изменение ширины более плавным, вы можете использовать QVariantAnimation:

from PySide2 import QtWidgets, QtCore


class MyTE(QtWidgets.QPlainTextEdit):
    def __init__(self):
        super(MyTE, self).__init__()

        self.setVerticalScrollBar(MyScrollBar(self))

        self.setPlainText("mmmmmmmmmmmmmmmmmmmmmmmmmmmmm" * 50)


class MyScrollBar(QtWidgets.QScrollBar):
    def __init__(self, parent=None):
        super(MyScrollBar, self).__init__(parent=parent)
        self._animation = QtCore.QVariantAnimation(
            startValue=10, endValue=25, duration=500
        )
        self._animation.valueChanged.connect(self.change_width)

    def enterEvent(self, event):
        super(MyScrollBar, self).enterEvent(event)
        self._animation.setDirection(QtCore.QAbstractAnimation.Forward)
        self._animation.start()

    def leaveEvent(self, event):
        super(MyScrollBar, self).leaveEvent(event)
        self._animation.setDirection(QtCore.QAbstractAnimation.Backward)
        self._animation.start()

    def change_width(self, width):
        self.setStyleSheet("""QScrollBar:vertical{ width: %dpx;}""" % (width,))


if __name__ == "__main__":

    app = QtWidgets.QApplication()
    wid = MyTE()
    wid.show()
    app.exec_()
person eyllanesc    schedule 22.10.2020
comment
Спасибо за этот ответ! Но если масштабирование работает с таблицей стилей, почему оно не работает с методом resize? Я бы предпочел сделать это таким образом, а не изменять таблицу стилей на лету. Кроме того, я пытаюсь использовать это в Autodesk Maya, но оно просто не масштабируется, есть идеи? - person Sneyek; 22.10.2020
comment
Я перепробовал много флагов виджетов, и, похоже, они не приходят оттуда, или, может быть, я пропустил один. Действительно любопытно, что таблица стилей в Maya действует иначе, чем в автономном Python, я также отчаянно пытался вызвать перерисовку, использовать setGeometry вместо изменения размера, изменить метод sizeHint и т. д., но, похоже, ничего не работает. - person Sneyek; 23.10.2020

.0 Наконец-то я нашел, как это сделать без использования таблицы стилей! Спасибо, eyllanesc, за ваш ответ, он дал мне новую основу для работы, я наконец-то понял, как Qt обрабатывает изменение размера (наверное), я использую setFixedSize в методе, вызываемом каждый раз, когда значение моей анимации изменяется.

Для работы необходимо иметь переопределенный метод sizeHint, возвращающий значение анимации для ширины. Кроме того, это работает в Autodesk Maya (к сожалению, решение, предложенное eyllanesc, не работает в Maya по неизвестной причине).

Есть мое решение:

from PySide2 import QtWidgets, QtCore, QtGui


class MyTE(QtWidgets.QPlainTextEdit):
    def __init__(self):
        super(MyTE, self).__init__()

        self.setVerticalScrollBar(MyScrollBar(self))

        self.setPlainText("mmmmmmmmmmmmmmmmmmmmmmmmmmmmm" * 50)
        self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)

    def resizeEvent(self, event):
        super(MyTE, self).resizeEvent(event)


class MyScrollBar(QtWidgets.QScrollBar):
    
    def __init__(self, parent=None):
        super(MyScrollBar, self).__init__(parent=parent)

        self._animation = QtCore.QVariantAnimation(
            startValue=10.0, endValue=25.0, duration=300
        )
        self._animation.valueChanged.connect(self.changeWidth)

        self._width = 10

    def enterEvent(self, event):
        super(MyScrollBar, self).enterEvent(event)
        self._animation.setDirection(QtCore.QAbstractAnimation.Forward)
        self._animation.start()

    def leaveEvent(self, event):
        super(MyScrollBar, self).leaveEvent(event)
        self._animation.setDirection(QtCore.QAbstractAnimation.Backward)
        self._animation.start()

    def sizeHint(self):
        """This must be overrided and return the width processed by the animation
        .. note:: In my final version I've replaced self.height() with 1 because it does not change 
        anything but self.height() was making my widget grow a bit each time.
        """
        return QtCore.QSize(self._width, self.height())

    def changeWidth(self, width):
        self._width = width  # This will allow us to return this value as the sizeHint width
        self.setFixedWidth(width) # This will ensure to scale the widget properly.


if __name__ == "__main__":

    app = QtWidgets.QApplication()
    wid = MyTE()
    wid.show()
    app.exec_()

Примечание. startValue и endValue QVariantAnimation должны быть с плавающей запятой, анимация не будет работать, если они имеют тип int (работает в Qt 5.6.1 (Maya 2018), но не в Qt 5.12.5 (Maya 2020).

PS: Если кого-то интересует мой окончательный виджет (полоса прокрутки в меню «Пуск» Windows 10), пусть спросит меня в частном порядке.

person Sneyek    schedule 24.10.2020