0

short version:
- take the code, run it, (all you need is two png icons)
- resize the window to be a lot larger
- dragndrop one of the icon far away (at least 300+ pixels away)
- then resize window back to original size
- then try to scroll to see the icon you dragndropped away.
- you will not be able. because scrollarea is too small.
- why?

long version:
i'm having trouble figuring how to update my scrollarea to reflect added or modified contents in my application.
i'm displaying icons, i can dragndrop them.
if i make the window bigger, dragndrop one icon to the bottom,
and then size back my window,
the scrollarea does not allow me to scroll to the bottom to see my icon.
basicaly, once the app started, scrollarea dimension never change.
how can i make the scrollarea, upon dragndrop, to update to new size ?
it could be bigger like shown in the screenshot below,
or smaller if all my icons are grouped in upper left corner for example..
if the content fit in the window, i will not show the slider.

here's a screenshot showing the problem,
it's the same window, i just resize it, and dragndrop one icon at the bottom:

scrollarea_not_updated (scrollarea is not updated, so i cannot scroll down to my icon i've put at the bottom)


here's the code so far:

#!/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_())
freeaks
  • 487
  • 2
  • 6
  • 14

1 Answers1

0

it turned out, i needed to modify dropEvent method,
to take the X and Y of the dropped icon and use those values for setMinimumSize().
like this:

#!/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_())

note the dropEvent() method of DragWidget() class.

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

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

so if the icon new position is greater than
the minimumSize (minimumWidth and minimumHeight),
then add the offset to self.minimumSize

thanks to Avaris from #pyqt channel for the help :)

freeaks
  • 487
  • 2
  • 6
  • 14