0

Could someone help me with the following issue. I am attempting to use Qrubberband to make a crop of an image.

The following code worked well and made a small popup appear with the photo inside which I could crop perfectly. The issue is I wanted the pop up to appear in the middle of a full screen window and not the top left of my screen. Adding the following lines acheived what I wanted.

self.showMaximized()
self.setAlignment(Qt.AlignCenter)

However, now I have centered the image and backed it in a full sized window. The crop coordinates no longer work as expected. Is there a simple fix for this ?

  import sys
    from PyQt5 import QtCore, QtGui
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    
    
    class Cropper (QLabel):
        def __init__(self, parentQWidget = None):
            super(Cropper, self).__init__(parentQWidget)
            self.showMaximized()
            self.initUI()
    
        def initUI (self):
            self.setPixmap(QtGui.QPixmap('dog4-300x178.png'))
            self.setAlignment(Qt.AlignCenter)
    
        def mousePressEvent (self, eventQMouseEvent):
            self.originQPoint = eventQMouseEvent.pos()
            self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self)
            self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, QtCore.QSize()))
            self.currentQRubberBand.show()
    
        def mouseMoveEvent (self, eventQMouseEvent):
            self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, eventQMouseEvent.pos()).normalized())
    
        def mouseReleaseEvent (self, eventQMouseEvent):
            self.currentQRubberBand.hide()
            currentQRect = self.currentQRubberBand.geometry()
            self.currentQRubberBand.deleteLater()
            cropQPixmap = self.pixmap().copy(currentQRect)
            cropQPixmap.save('output.png')
            print("saved crop")
    
    if __name__ == '__main__':
        myQApplication = QApplication(sys.argv)
        myCropper = Cropper()
        myCropper.show()
        sys.exit(myQApplication.exec_())

Thank you :-)

Clive Atkins
  • 545
  • 4
  • 21
  • 1
    You have a **huge** problem: you are naming your class `QRubberBand`, then you're attempting to use it within the mouse press event, which will not create a *real* QRubberBand, but your own class again. Start by naming the subclass with a valid, *unique* name. – musicamante Aug 06 '23 at 18:16
  • @musicamante I have done so and amended the post. thanks – Clive Atkins Aug 06 '23 at 18:18
  • Mouse positions are always relative to the *widget*. The pixmap is just *part* of its content, you need to map the coordinates considering the alignment, so *translate* the rubber band geometry to the origin point of the image; if the image is centered (and assuming that you didn't scale its contents), this is just a matter of `(width - self.pixmap().width()) // 2` for the x and `(height - self.pixmap().height()) // 2)`; then you can just subtract those values from the rubber band geometry and you'll get the real rectangle of the image. – musicamante Aug 06 '23 at 18:32
  • 1
    Alternatively, get the rectangle of the image, and move its center to that of the widget: `rect = self.pixmap().rect()` `rect.moveCenter(self.rect().center())`, then you can translate the rubber band geometry by subtracting the top left corner of that rectangle: `currentQRect.translate(-rect.topLeft())`. Note that if you plan some sort of image editor or annotator, you should consider looking into the [Graphics View Framework](https://doc.qt.io/qt-5/graphicsview.html) instead: while more complex than a basic approach as yours, it actually makes all these problems much simpler to solve. – musicamante Aug 06 '23 at 18:40
  • @musicamante thank you for taking time out of your day to help me I tried the second answer you gave me but it doesnt seem to center or work correctly. Was this what you meant? https://pastebin.com/rf8fvxdk – Clive Atkins Aug 06 '23 at 19:17
  • 1
    That code has *lots* of issues in that code. 1. [`rect`](//doc.qt.io/qt-5/qwidget.html#rect-prop) is a property of all QWidgets, with `self.rect()` being its *getter* function; as such, should **never** be overwritten, especially when you plan to use it (as you try to do with `self.rect()` just two lines after); 2. widgets can be resized even, while initialized, so moving the center of that rectangle within the `__init__` is *wrong*, as it should only be done when the "selection" is being computed; 3. the above is done assuming that the image is centered, but you commented `setAlignment()`; »» – musicamante Aug 06 '23 at 19:31
  • 1
    »» 4. please read more carefully, as I specifically wrote that the origin point of the centered rectangle must be **subtracted**, but you ignored the minus sign in `currentQRect.translate(-rect.topLeft())`, which is necessary to position the translated rectangle based on the actual image coordinates. Note that whenever you need to work with generic coordinate systems, proper awareness and knowledge of basic Euclidean geometry principles is mandatory. Please take your time to read the post for which I marked your question as a duplicate, as it contains useful insights on how all this works. – musicamante Aug 06 '23 at 19:36
  • @musicamante I got it thanks so much moved the stated lines to mouseReleaseEvent and it works great now. Your the best :-) I did read the example you stated and it was a good demo but I couldnt work out how to pass the recalculated coordinates back into my original functions. Ill have another look this evening though. – Clive Atkins Aug 06 '23 at 19:40

0 Answers0