I have a "working" example but a few issues I can not seem to figure out. I have calculated the topLeft and bottomRight coordinates inside the rubber band and converted them to the widgets coordinates. However the pixmap.copy() does not copy those coordinates. The shifting global and widget coordinates systems are confusing so I assume I am not understanding the coordinate system properly.
import sys
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QRubberBand
from PyQt5.QtWidgets import QHBoxLayout, QSizeGrip
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(526, 478)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
self.gridLayout.setContentsMargins(11, 11, 11, 11)
self.gridLayout.setSpacing(6)
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(self.centralWidget)
self.label.setText("")
self.label.setPixmap(QtGui.QPixmap("ava.jpg"))
self.label.setScaledContents(True)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.pushButton = QtWidgets.QPushButton(self.centralWidget)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 526, 25))
self.menuBar.setObjectName("menuBar")
MainWindow.setMenuBar(self.menuBar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Resizeable QLabel Example"))
self.pushButton.setText(_translate("MainWindow", "Crop"))
class Test_Resize(QMainWindow, Ui_MainWindow):
def __init__(self):
super(Test_Resize, self).__init__()
self.setupUi(self)
self.show()
def run(self):
self.pushButton.clicked.connect(self.cropImage)
self.band = Resizable_rubber_band(self.label)
self.band.move(50, 50)
self.band.resize(200, 266)
self.band.setMinimumSize(30, 30)
def cropImage(self):
rect = self.band.getCoverage()
# copy(QRect( x, y, width, height))
self.label.setPixmap(self.label.pixmap().copy(rect))
self.label.repaint()
class Resizable_rubber_band(QWidget):
def __init__(self, parent=None):
super(Resizable_rubber_band, self).__init__(parent)
# tell QSizeGrip to resize this widget instead of top-level window
self.setWindowFlags(Qt.SubWindow)
self.layout = QHBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.grip1 = QSizeGrip(self)
self.grip2 = QSizeGrip(self)
self.layout.addWidget(self.grip1, 0, Qt.AlignLeft | Qt.AlignTop)
self.layout.addWidget(self.grip2, 0, Qt.AlignRight | Qt.AlignBottom)
self.rubberband = QRubberBand(QRubberBand.Rectangle, self)
self.rubberband.move(0, 0)
self.rubberband.show()
self.show()
def resizeEvent(self, event):
# force 4h x 3w aspect ratio using QSize
# if width < height: height = 1.333 * width
# if width > height: width = height / 1.333
# width = self.width()
# height = self.height()
# if(width < height):
# height = int(width * 1.333)
# else:
# width = int(height / 1.333)
# self.rubberband.resize(QSize(width, height))
self.rubberband.resize(self.size())
def getCoverage(self):
localCoords = self.contentsRect()
print("localCoords: ", localCoords)
TL = self.mapToGlobal(localCoords.topLeft())
BR = localCoords.bottomRight()
# TL+BR to get width & height
widgetCoords = QRect(TL, TL+BR)
print("widgetCoords: ", widgetCoords)
return widgetCoords
app = QApplication(sys.argv)
program = Test_Resize()
program.run()
sys.exit(app.exec_())
When you enable the scaledContents property:
self.label.setScaledContents(True)
the coordinates of the QPixmap
do not correspond to the pixels of the QLabel
, for example let's say that the QPixmap
is 20 by 20 and the QLabel
20 by 10 then it will have to be transformed at the height of the pixmap from 20 to 10.
The first thing to do is to convert global coordinates to QLabel
coordinates with self.label.mapFromGlobal()
, and then transform the rectangle by scaling the width and height proportional to the initial change.
def cropImage(self):
rect = self.band.getCoverage()
r = QRect(self.label.mapFromGlobal(rect.topLeft()), rect.size())
px = self.label.pixmap()
tr = QtGui.QTransform()
tr.scale(px.size().width()*1.0/self.label.size().width(), px.size().height()*1.0/self.label.size().height())
r = tr.mapRect(r)
self.label.setPixmap(px.copy(r))