Use mouse drag to change the width of a rectangle?

Hello everyone,
I got this code that helps me draw a rectangle, I’d like to be able to drag the left and right sides of the rectangle to adjust the width of the rectangle, make the rectangle behave in a way similar to how you crop an image on most photo editing software, where you draw the initial area but you have the possibility to adjust the width afterwards to get the crop you want.
Thank you for your help.
the code I have so far:

import sys

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


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(30,30,600,400)
        self.begin = QPoint()
        self.end = QPoint()
        self.show()

    def paintEvent(self, event):
        qp = QPainter(self)
        br = QBrush(QColor(100, 10, 10, 40))  
        qp.setBrush(br)   
        qp.drawRect(QRect(self.begin, self.end))       

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        # print(f"press begin {self.begin}")
        # print(f"press end   {self.end}")
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        #self.begin = event.pos()
        self.end = event.pos()
        #self.update()
        print(f"begin {self.begin}")
        print(f"end   {self.end}")



if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    window.show()
    app.aboutToQuit.connect(app.deleteLater)
    sys.exit(app.exec_())

Hi,
maybe it’s a late response, but hopefully it will benefit someone.

I will build on your code, will try to make my code very explicit, to be easy to understand.

Next, you will find 2 code blocks:

  1. the code with resize working as intended.
  2. same as the first one, but added the visual feed back part, to make it feel professional :smiley: .

Final Result:

image



First Block

import sys

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


FREE_STATE = 1
BUILDING_SQUARE = 2
BEGIN_SIDE_EDIT = 3
END_SIDE_EDIT = 4


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(30, 30, 600, 400)
        self.begin = QPoint()
        self.end = QPoint()

        self.state = FREE_STATE

    def paintEvent(self, event):
        qp = QPainter(self)
        br = QBrush(QColor(100, 10, 10, 40))
        qp.setBrush(br)
        qp.drawRect(QRect(self.begin, self.end))

    def mousePressEvent(self, event):
        if not self.begin.isNull() and not self.end.isNull():
            p = event.pos()
            y1, y2 = sorted([self.begin.y(), self.end.y()])
            if y1 <= p.y() <= y2:
                # 3 resolution, more easy to pick than 1px
                if abs(self.begin.x() - p.x()) <= 3:
                    self.state = BEGIN_SIDE_EDIT
                    return
                elif abs(self.end.x() - p.x()) <= 3:
                    self.state = END_SIDE_EDIT
                    return
        self.state = BUILDING_SQUARE

        self.begin = event.pos()
        self.end = event.pos()
        self.update()

    def applye_event(self, event):

        if self.state == BUILDING_SQUARE:
            self.end = event.pos()
        elif self.state == BEGIN_SIDE_EDIT:
            self.begin.setX(event.x())
        elif self.state == END_SIDE_EDIT:
            self.end.setX(event.x())

    def mouseMoveEvent(self, event):
        self.applye_event(event)
        self.update()

    def mouseReleaseEvent(self, event):
        self.applye_event(event)
        self.state = FREE_STATE


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    window.show()
    app.aboutToQuit.connect(app.deleteLater)
    sys.exit(app.exec_())


Second Block

import sys

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


FREE_STATE = 1
BUILDING_SQUARE = 2
BEGIN_SIDE_EDIT = 3
END_SIDE_EDIT = 4


CURSOR_ON_BEGIN_SIDE = 1
CURSOR_ON_END_SIDE = 2


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(30, 30, 600, 400)
        self.begin = QPoint()
        self.end = QPoint()

        self.state = FREE_STATE

        self.setMouseTracking(True)
        self.free_cursor_on_side = 0

    def paintEvent(self, event):
        qp = QPainter(self)
        br = QBrush(QColor(100, 10, 10, 40))
        qp.setBrush(br)
        qp.drawRect(QRect(self.begin, self.end))

        if not self.free_cursor_on_side:
            return

        qp.setPen(QPen(Qt.black, 5, Qt.DashLine))
        if self.free_cursor_on_side == CURSOR_ON_BEGIN_SIDE:
            end = QPoint(self.end)
            end.setX(self.begin.x())
            qp.drawLine(self.begin, end)

        elif self.free_cursor_on_side == CURSOR_ON_END_SIDE:
            begin = QPoint(self.begin)
            begin.setX(self.end.x())
            qp.drawLine(self.end, begin)

    def cursor_on_side(self, e_pos) -> int:
        if not self.begin.isNull() and not self.end.isNull():
            y1, y2 = sorted([self.begin.y(), self.end.y()])
            if y1 <= e_pos.y() <= y2:

                # 5 resolution, more easy to pick than 1px
                if abs(self.begin.x() - e_pos.x()) <= 5:
                    return CURSOR_ON_BEGIN_SIDE
                elif abs(self.end.x() - e_pos.x()) <= 5:
                    return CURSOR_ON_END_SIDE

        return 0

    def mousePressEvent(self, event):
        side = self.cursor_on_side(event.pos())
        if side == CURSOR_ON_BEGIN_SIDE:
            self.state = BEGIN_SIDE_EDIT
        elif side == CURSOR_ON_END_SIDE:
            self.state = END_SIDE_EDIT
        else:
            self.state = BUILDING_SQUARE

            self.begin = event.pos()
            self.end = event.pos()
            self.update()

    def applye_event(self, event):

        if self.state == BUILDING_SQUARE:
            self.end = event.pos()
        elif self.state == BEGIN_SIDE_EDIT:
            self.begin.setX(event.x())
        elif self.state == END_SIDE_EDIT:
            self.end.setX(event.x())

    def mouseMoveEvent(self, event):
        if self.state == FREE_STATE:
            self.free_cursor_on_side = self.cursor_on_side(event.pos())
            if self.free_cursor_on_side:
                self.setCursor(Qt.SizeHorCursor)
            else:
                self.unsetCursor()
            self.update()
        else:
            self.applye_event(event)
            self.update()

    def mouseReleaseEvent(self, event):
        self.applye_event(event)
        self.state = FREE_STATE


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    window.show()
    app.aboutToQuit.connect(app.deleteLater)
    sys.exit(app.exec_())

regards.