Pyqt scrollarea не изменен после dragndrop

Укороченная версия:
- возьмите код, запустите его, (все, что вам нужно, это две иконки PNG)
- изменить размер окна, чтобы быть намного больше
- перетащите один из значков далеко (как минимум на 300+ пикселей)
- затем измените размер окна до исходного размера
- затем попробуйте прокрутить, чтобы увидеть значок, который вы перетащили.
- ты не сможешь. потому что скроллария слишком мала.
- Зачем?

длинная версия:
У меня возникли проблемы с выяснением, как обновить прокрутку для отображения добавленного или измененного содержимого в моем приложении.
Я показываю значки, я могу перетаскивать их.
если я увеличу окно, перетащите один значок вниз,
а затем верни мне размер окна,
скролларея не позволяет мне прокручиваться вниз, чтобы увидеть мою иконку.
По сути, после запуска приложения размеры scrollarea никогда не меняются.
Как я могу сделать скроллареа на dragndrop, чтобы обновиться до нового размера?
это может быть больше, как показано на скриншоте ниже,
или меньше, если все мои значки сгруппированы в верхнем левом углу, например..
если содержимое помещается в окне, я не буду показывать слайдер.

вот скриншот, показывающий проблему,
это то же самое окно, я просто изменяю его размер и перетаскиваю один значок внизу:

scrollarea_not_updated(scrollarea не обновляется, поэтому я не могу прокрутить вниз к значку, который я поместил внизу)


вот код на данный момент:

#!/usr/bin/python3

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys


class DragWidget(QFrame):

    def __init__(self, parent=None):
        super(DragWidget, self).__init__(parent)

        self.setMinimumSize(200, 200)
        self.setAcceptDrops(True)

        test_icon1 = QLabel(self)
        test_icon1.setPixmap(QPixmap('./images/closeicon.png'))
        test_icon1.move(20, 20)
        test_icon1.show()
        test_icon1.setAttribute(Qt.WA_DeleteOnClose)

        test_icon2 = QLabel(self)
        test_icon2.setPixmap(QPixmap('./images/openicon.png'))
        test_icon2.move(60, 20)
        test_icon2.show()
        test_icon2.setAttribute(Qt.WA_DeleteOnClose)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat('application/x-dnditemdata'):
            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.acceptProposedAction()
        else:
            event.ignore()

    dragMoveEvent = dragEnterEvent

    def dropEvent(self, event):
        if event.mimeData().hasFormat('application/x-dnditemdata'):
            itemData = event.mimeData().data('application/x-dnditemdata')
            dataStream = QDataStream(itemData, QIODevice.ReadOnly)

            pixmap = QPixmap()
            offset = QPoint()
            dataStream >> pixmap >> offset

            newIcon = QLabel(self)
            newIcon.setPixmap(pixmap)
            newIcon.move(event.pos() - offset)
            newIcon.show()
            newIcon.setAttribute(Qt.WA_DeleteOnClose)

            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.acceptProposedAction()
        else:
            event.ignore()

    def mousePressEvent(self, event):
        child = self.childAt(event.pos())
        if not child:
            return

        pixmap = QPixmap(child.pixmap())

        itemData = QByteArray()
        dataStream = QDataStream(itemData, QIODevice.WriteOnly)
        dataStream << pixmap << QPoint(event.pos() - child.pos())

        mimeData = QMimeData()
        mimeData.setData('application/x-dnditemdata', itemData)

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setPixmap(pixmap)
        drag.setHotSpot(event.pos() - child.pos())

        tempPixmap = QPixmap(pixmap)
        painter = QPainter()
        painter.begin(tempPixmap)
        painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127))
        painter.end()

        child.setPixmap(tempPixmap)
        if drag.exec_(Qt.CopyAction | Qt.MoveAction) == Qt.MoveAction:
            child.close()
        else:
            child.show()
            child.setPixmap(pixmap)


class Window(QWidget):

    def __init__(self, parent=None):
        super(Window, self).__init__()

        widget = QWidget()
        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(QPixmap("images/pattern.png")))
        widget.setPalette(palette)
        layout = QVBoxLayout(self)
        layout.addWidget(DragWidget())
        widget.setLayout(layout)
        scroll = QScrollArea()
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        scroll.setWidget(widget)
        vlayout = QVBoxLayout(self)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.setSpacing(0)
        vlayout.addWidget(scroll)
        self.setLayout(vlayout)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window('./')
    sys.exit(app.exec_())

1 ответ

Оказалось, мне нужно изменить метод dropEvent,
взять X и Y отброшенного значка и использовать эти значения для setMinimumSize().
как это:

#!/usr/bin/python3

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys


class DragWidget(QFrame):

    def __init__(self, parent=None):
        super(DragWidget, self).__init__(parent)

        self.setMinimumSize(200, 200)
        self.setAcceptDrops(True)

        self.test_icon1 = QLabel(self)
        self.test_icon1.setPixmap(QPixmap('./images/closeicon.png'))
        self.test_icon1.move(20, 20)
        self.test_icon1.show()
        self.test_icon1.setAttribute(Qt.WA_DeleteOnClose)

        self.test_icon2 = QLabel(self)
        self.test_icon2.setPixmap(QPixmap('./images/openicon.png'))
        self.test_icon2.move(60, 20)
        self.test_icon2.show()
        self.test_icon2.setAttribute(Qt.WA_DeleteOnClose)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat('application/x-dnditemdata'):
            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.acceptProposedAction()
        else:
            event.ignore()

    dragMoveEvent = dragEnterEvent

    def dropEvent(self, event):
        if event.mimeData().hasFormat('application/x-dnditemdata'):
            itemData = event.mimeData().data('application/x-dnditemdata')
            dataStream = QDataStream(itemData, QIODevice.ReadOnly)

            pixmap = QPixmap()
            offset = QPoint()
            dataStream >> pixmap >> offset

            newIcon = QLabel(self)
            newIcon.setPixmap(pixmap)
            newIcon.move(event.pos() - offset)
            newIcon.show()
            newIcon.setAttribute(Qt.WA_DeleteOnClose)

            if newIcon.y()+32 > self.minimumHeight():
                self.setMinimumHeight(newIcon.y()+32)

            if newIcon.x()+32 > self.minimumWidth():
                self.setMinimumWidth(newIcon.x()+32)

            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.acceptProposedAction()
        else:
            event.ignore()

    def mousePressEvent(self, event):
        child = self.childAt(event.pos())
        if not child:
            return

        pixmap = QPixmap(child.pixmap())

        itemData = QByteArray()
        dataStream = QDataStream(itemData, QIODevice.WriteOnly)
        dataStream << pixmap << QPoint(event.pos() - child.pos())

        mimeData = QMimeData()
        mimeData.setData('application/x-dnditemdata', itemData)

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setPixmap(pixmap)
        drag.setHotSpot(event.pos() - child.pos())

        tempPixmap = QPixmap(pixmap)
        painter = QPainter()
        painter.begin(tempPixmap)
        painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127))
        painter.end()

        child.setPixmap(tempPixmap)
        if drag.exec_(Qt.CopyAction | Qt.MoveAction) == Qt.MoveAction:
            child.close()
        else:
            child.show()
            child.setPixmap(pixmap)


class Window(QWidget):

    def __init__(self, parent=None):
        super(Window, self).__init__()
        self.pattern = "images/pattern.png"
        self.widget = QWidget()
        self.palette = QPalette()
        self.palette.setBrush(QPalette.Background, QBrush(QPixmap(self.pattern)))
        self.widget.setPalette(self.palette)
        layout = QVBoxLayout(self)
        layout.addWidget(DragWidget())
        self.widget.setLayout(layout)
        scroll = QScrollArea()
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setWidgetResizable(True)
        scroll.setWidget(self.widget)
        vlayout = QVBoxLayout(self)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.setSpacing(0)
        vlayout.addWidget(scroll)
        self.setLayout(vlayout)
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window('./')
    window2 = Window('./')
    sys.exit(app.exec_())

обратите внимание на метод dropEvent() класса DragWidget().

    if newIcon.y()+32 > self.minimumHeight():
        self.setMinimumHeight(newIcon.y()+32)

    if newIcon.x()+32 > self.minimumWidth():
        self.setMinimumWidth(newIcon.x()+32)

поэтому, если значок новой позиции больше, чем
The MinimumSize (MinimumWidth и MinimHeight),
затем добавьте смещение к self.minimumSize

спасибо Avaris из канала #pyqt за помощь:)

Другие вопросы по тегам