Python – QRubberBand that selects an area from an image but can be resized, does not resize

QRubberBand that selects an area from an image but can be resized, does not resize… here is a solution to the problem.

QRubberBand that selects an area from an image but can be resized, does not resize

I want to select an area from the image and get her coordinates. I’ve searched and found this code, it creates the object rubberband but I can’t resize it: Strangely, on Linux (lubuntu) it works but not on macOS: I want a rubberband version on macOS These “handles” are special, and in macOS, none of these handles will appear. One more thing, I can move the rubber band rectangle by right-clicking on the selection and then move the image wherever I want, just like moving the icon on the desktop. Thanks!

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class ResizableRubberBand(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ResizableRubberBand, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.SubWindow)
        layout = QtWidgets.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(
            QtWidgets.QSizeGrip(self), 0,
            QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
        layout.addWidget(
            QtWidgets.QSizeGrip(self), 0,
            QtCore.Qt.AlignRight | QtCore.Qt.AlignBottom)
        self._band = QtWidgets.QRubberBand(
            QtWidgets.QRubberBand.Rectangle, self)
        self._band.show()
        self.show()

def resizeEvent(self, event):
        size = QtCore.QSize(3, 4)
        size.scale(self.size(), QtCore.Qt.KeepAspectRatio)
        self.resize(size)
        self._band.resize(self.size())

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtWidgets.QPushButton('Show Rubber Band')
        self.button.clicked.connect(self.handleButton)
        self.label = QtWidgets.QLabel()
        self.label.setScaledContents(True)
        self.label.setPixmap(QtGui.QPixmap('image. JPG'))
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.button)

def handleButton(self):
        self.band = ResizableRubberBand(self.label)
        self.band.setGeometry(50, 50, 150, 300)

if __name__ == '__main__':

app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(800, 100, 600, 500)
    window.show()
    sys.exit(app.exec_())

Solution

You are on the right track. PyQt uses event handlers to control mouse interaction and resize events. For your application, resizing handle will call resizeEvent and redraw the changed window involving the paintEvent. Similarly, mouse interactions use mousePressEvent, mouseMoveEvent, and mouseReleaseEvent as event handlers that are invoked when these actions are triggered. Essentially, for each interaction you want, you must implement a handler for that particular event. I tested with PyQt4, but PyQt5 should be the same.

enter image description here

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# from PyQt4 import QtCore, QtWidgets

class ResizableRubberBand(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ResizableRubberBand, self).__init__(parent)

self.draggable = True
        self.dragging_threshold = 5
        self.mousePressPos = None
        self.mouseMovePos = None
        self.borderRadius = 5

self.setWindowFlags(QtCore.Qt.SubWindow)
        layout = QtWidgets.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(
            QtWidgets.QSizeGrip(self), 0,
            QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
        layout.addWidget(
            QtWidgets.QSizeGrip(self), 0,
            QtCore.Qt.AlignRight | QtCore.Qt.AlignBottom)
        self._band = QtWidgets.QRubberBand(
            QtWidgets.QRubberBand.Rectangle, self)
        self._band.show()
        self.show()

def resizeEvent(self, event):
        self._band.resize(self.size())

def paintEvent(self, event):
        # Get current window size
        window_size = self.size()
        qp = QtWidgets.QPainter()
        qp.begin(self)
        qp.setRenderHint(QtWidgets.QPainter.Antialiasing, True)
        qp.drawRoundedRect(0, 0, window_size.width(), window_size.height(),
                           self.borderRadius, self.borderRadius)
        qp.end()

def mousePressEvent(self, event):
        if self.draggable and event.button() == QtCore.Qt.RightButton:
            self.mousePressPos = event.globalPos()                # global
            self.mouseMovePos = event.globalPos() - self.pos()    # local
        super(ResizableRubberBand, self).mousePressEvent(event)

def mouseMoveEvent(self, event):
        if self.draggable and event.buttons() & QtCore.Qt.RightButton:
            globalPos = event.globalPos()
            moved = globalPos - self.mousePressPos
            if moved.manhattanLength() > self.dragging_threshold:
                # Move when user drag window more than dragging_threshold
                diff = globalPos - self.mouseMovePos
                self.move(diff)
                self.mouseMovePos = globalPos - self.pos()
        super(ResizableRubberBand, self).mouseMoveEvent(event)

def mouseReleaseEvent(self, event):
        if self.mousePressPos is not None:
            if event.button() == QtCore.Qt.RightButton:
                moved = event.globalPos() - self.mousePressPos
                if moved.manhattanLength() > self.dragging_threshold:
                    # Do not call click event or so on
                    event.ignore()
                self.mousePressPos = None
        super(ResizableRubberBand, self).mouseReleaseEvent(event)

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtWidgets.QPushButton('Show Rubber Band')
        self.button.clicked.connect(self.handleButton)
        self.label = QtWidgets.QLabel()
        self.label.setScaledContents(True)
        self.label.setPixmap(QtGui.QPixmap('image. JPG'))
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.button)

def handleButton(self):
        self.band = ResizableRubberBand(self.label)
        self.band.setGeometry(150, 150, 150, 150)

if __name__ == '__main__':

app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(800, 100, 600, 500)
    window.show()
    sys.exit(app.exec_())

Related Problems and Solutions