Nourah
Nourah

Reputation: 23

Using draggable QLabel for cropping

I am trying to use draggable QLabels for cropping an image. The basic idea of my code is segmenting characters in an image.

So, the user will first upload an image contains three characters to the program. Then, he will specify the type of the plate, is it short or long by checking on the suitable checkbox. Based on the checkbox, three boxes (QLabels) with specific size will be shown to the user so he can drag each box on a character in the uploaded image. Once the user is finished placing the boxes on the characters, he can click on the Segment button to apply the cropping operation. The final result will be three images of the cropped characters.

I have done from the program interface and some functionality, However, I'm facing a problem with darg and cropping functions. I have tried different ways with dragging boxes (QLabels) but it's not working with me. I really don't know what's wrong in my code :(. I'm using python 3 and PyQt5.

This is my code:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QCursor
from PyQt5.QtCore import QMimeData, Qt

boxes_items = []

#=========================================================================
class DraggableLabel(QLabel):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setAcceptDrops(True)


    def dragEnterEvent(self, event):
        event.accept()
        event.acceptProposedAction()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            event.accept()
            self.setCursor(QtGui.QCursor(QtCore.Qt.CloseHandCursor))
            self.drag_start_position = event.pos()


    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
           return
        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
           return
        drag = QDrag(self)
        '''
        mimedata = QMimeData()
        #mimedata.setText(self.text())
        mimedata.setImageData(self.pixmap().toImage())
        drag.setMimeData(mimedata)
        pixmap = QPixmap(self.size())
        painter = QPainter(pixmap)
        painter.drawPixmap(self.rect(), self.grab())
        painter.end()
        drag.setPixmap(pixmap)
        '''

        drag.setHotSpot(event.pos())
        drag.exec_(Qt.CopyAction | Qt.MoveAction)

'''
class DropLabel(QLabel):
    def __init__(self, *args, **kwargs):
        QLabel.__init__(self, *args, **kwargs)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasText():
            event.acceptProposedAction()

    def dropEvent(self, event):
        pos = event.pos()
        self.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
       # text = event.mimeData().text()
       # self.setText(text)
        event.acceptProposedAction()
'''

#==========================================================================
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.setEnabled(True)
        Dialog.resize(1050, 800)
        Dialog.setMinimumSize(QtCore.QSize(1050, 800))
        Dialog.setMaximumSize(QtCore.QSize(1050, 800))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("../.designer/backup/project pic/images LPR icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        Dialog.setWindowIcon(icon)
        Dialog.setStyleSheet("background-color: rgb(217, 217, 217);\n"
"background-color: rgb(243, 243, 243);")
       # Dialog.setAcceptDrops(True)

        self.BrowesImageButton = QtWidgets.QPushButton(Dialog)
        self.BrowesImageButton.setGeometry(QtCore.QRect(820, 60, 200, 60))
        self.BrowesImageButton.setMinimumSize(QtCore.QSize(200, 60))
        self.BrowesImageButton.setMaximumSize(QtCore.QSize(200, 60))
        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei UI")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)

        self.BrowesImageButton.setFont(font)
        self.BrowesImageButton.setStyleSheet("")
        self.BrowesImageButton.setCheckable(True)
        self.BrowesImageButton.setObjectName("BrowesImageButton")
        self.groupBox = QtWidgets.QGroupBox(Dialog)
        self.groupBox.setGeometry(QtCore.QRect(820, 170, 211, 131))
        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei")
        font.setPointSize(10)
        font.setBold(True)

        font.setWeight(75)
        self.groupBox.setFont(font)
        self.groupBox.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.groupBox.setObjectName("groupBox")
        self.checkBox_Short = QtWidgets.QCheckBox(self.groupBox)
        self.checkBox_Short.setGeometry(QtCore.QRect(20, 40, 141, 23))
        font = QtGui.QFont()
        font.setFamily("Adobe Heiti Std R")
        font.setPointSize(10)

        self.checkBox_Short.setFont(font)
        self.checkBox_Short.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.checkBox_Short.setObjectName("checkBox_Short")
        self.checkBox_Short.setEnabled(False)

        self.checkBox_Long = QtWidgets.QCheckBox(self.groupBox)
        self.checkBox_Long.setGeometry(QtCore.QRect(20, 80, 141, 23))
        font = QtGui.QFont()
        font.setFamily("Adobe Heiti Std R")
        font.setPointSize(10)

        self.checkBox_Long.setFont(font)
        self.checkBox_Long.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.checkBox_Long.setObjectName("checkBox_Long")
        self.checkBox_Long.setEnabled(False)

        self.SegmentImageButton = QtWidgets.QPushButton(Dialog)
        self.SegmentImageButton.setGeometry(QtCore.QRect(830, 330, 200, 60))
        self.SegmentImageButton.setMinimumSize(QtCore.QSize(200, 60))
        self.SegmentImageButton.setMaximumSize(QtCore.QSize(200, 60))

        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei UI")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.SegmentImageButton.setFont(font)
        self.SegmentImageButton.setStyleSheet("")
        self.SegmentImageButton.setCheckable(True)
        self.SegmentImageButton.setObjectName("SegmentImageButton")
        self.SegmentImageButton.setEnabled(False)

        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(980, 470, 45, 45))
        self.label.setMinimumSize(QtCore.QSize(45, 45))
        self.label.setMaximumSize(QtCore.QSize(45, 45))
        self.label.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self.label.setMouseTracking(True)
        self.label.setAcceptDrops(True)
        self.label.setStyleSheet("border-color: rgb(238, 0, 0); border-width : 2.0px; border-style:inset;")
        self.label.setFrameShape(QtWidgets.QFrame.Box)
        self.label.setLineWidth(2)
        self.label.setText("")
        self.label.setObjectName("label")
        self.label.hide()

        '''
        self.graphicsView = QtWidgets.QGraphicsView(Dialog)
        self.graphicsView.setGeometry(QtCore.QRect(40, 40, 750, 500))
        self.graphicsView.setMinimumSize(QtCore.QSize(750, 500))
        self.graphicsView.setMaximumSize(QtCore.QSize(750, 500))
        self.graphicsView.setObjectName("graphicsView")
        '''
        self.UserImageLbl = QtWidgets.QLabel(Dialog)
        self.UserImageLbl.setGeometry(QtCore.QRect(40, 40, 750, 500))
        self.UserImageLbl.setMinimumSize(QtCore.QSize(750, 500))
        self.UserImageLbl.setMaximumSize(QtCore.QSize(750, 500))
        self.UserImageLbl.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.UserImageLbl.setFrameShadow(QtWidgets.QFrame.Plain)
        self.UserImageLbl.setLineWidth(1)
        self.UserImageLbl.setMidLineWidth(0)
        self.UserImageLbl.setText("")
        self.UserImageLbl.setScaledContents(False)
        self.UserImageLbl.setAlignment(QtCore.Qt.AlignCenter)
        self.UserImageLbl.setObjectName("UserImageLbl")



        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(900, 470, 45, 45))
        self.label_2.setMinimumSize(QtCore.QSize(45, 45))
        self.label_2.setMaximumSize(QtCore.QSize(45, 45))
        self.label_2.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self.label_2.setMouseTracking(True)
        self.label_2.setAcceptDrops(True)
        self.label_2.setStyleSheet("border-color: rgb(238, 0, 0); border-width : 2.0px; border-style:inset;")
        self.label_2.setFrameShape(QtWidgets.QFrame.Box)
        self.label_2.setLineWidth(2)
        self.label_2.setText("")
        self.label_2.setObjectName("label_2")
        self.label_2.hide()

        self.label_3 = QtWidgets.QLabel(Dialog)
        self.label_3.setGeometry(QtCore.QRect(820, 470, 45, 45))
        self.label_3.setMinimumSize(QtCore.QSize(45, 45))
        self.label_3.setMaximumSize(QtCore.QSize(45, 45))
        self.label_3.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self.label_3.setMouseTracking(True)
        self.label_3.setAcceptDrops(True)
        self.label_3.setStyleSheet("border-color: rgb(238, 0, 0); border-width : 2.0px; border-style:inset;")
        self.label_3.setFrameShape(QtWidgets.QFrame.Box)
        self.label_3.setLineWidth(2)
        self.label_3.setText("")
        self.label_3.setObjectName("label_3")
        self.label_3.hide()

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

        #Add labels in boxes items list 

        boxes_items.append(self.label)
        boxes_items.append(self.label_2)
        boxes_items.append(self.label_3)



#=================================== Calling =======================

        self.BrowesImageButton.clicked.connect(self.setImage)
        self.checkBox_Short.stateChanged.connect(self.Change_the_Checkbox_Short_Function)
        self.checkBox_Long.stateChanged.connect(self.Change_the_Checkbox_Long_Function)

#=================================== Functions =========================

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Cropping"))
        self.BrowesImageButton.setText(_translate("Dialog", "Select Image"))
        self.groupBox.setTitle(_translate("Dialog", "Choose Plate Type"))
        self.checkBox_Short.setText(_translate("Dialog", "Short Plate"))
        self.checkBox_Long.setText(_translate("Dialog", "Long Plate"))
        self.SegmentImageButton.setText(_translate("Dialog", "Segment Image"))
        self.label.setToolTip(_translate("Dialog", "Drage it on first charecter"))
        self.label_2.setToolTip(_translate("Dialog", "Drage it on second charecter"))
        self.label_3.setToolTip(_translate("Dialog", "Drage it on third charecter"))

#=================================================================================

    def setImage(self):
        fileName, _ = QtWidgets.QFileDialog.getOpenFileName(None, "Select Image", "", "Image Files (*.png *.jpg *jpeg *.bmp)") # Ask for file

        if fileName: # If the user gives a file
            pixmap = QtGui.QPixmap(fileName) # Setup pixmap with the provided image
            pixmap = pixmap.scaled(self.UserImageLbl.width(), self.UserImageLbl.height(), QtCore.Qt.KeepAspectRatio) # Scale pixmap
            self.UserImageLbl.setPixmap(pixmap) # Set the pixmap onto the label
            self.UserImageLbl.setAlignment(QtCore.Qt.AlignCenter) # Align the label to center
            #UserImageLbl = DropLabel()
            self.checkBox_Short.setEnabled(True)
            self.checkBox_Long.setEnabled(True)

#=====================================================================    
    def Change_the_Checkbox_Short_Function(self):
        if self.checkBox_Short.isChecked(): 
           self.checkBox_Long.setEnabled(False)
           self.SegmentImageButton.setEnabled(True)

           # Presenting 3 boxes 45x45 hear
           for box_item in boxes_items:
               #box_item.setDragEnabled(True) 
               box_item.setMinimumSize(QtCore.QSize(45, 45))
               box_item.setMaximumSize(QtCore.QSize(45, 45))
               box_item.show()
               box_item = DraggableLabel()  


        else:
           self.checkBox_Long.setEnabled(True)
           for box_item in boxes_items:
               box_item.hide()

#========================================================================       

    def Change_the_Checkbox_Long_Function(self):
        if self.checkBox_Long.isChecked(): 
           self.checkBox_Short.setEnabled(False)
           self.SegmentImageButton.setEnabled(True)

           # Presenting 3 boxes 40x40 hear
           for box_item in boxes_items:
              # box_item.setDragEnabled(True) 
               box_item.setMinimumSize(QtCore.QSize(40, 40))
               box_item.setMaximumSize(QtCore.QSize(40, 40))
               box_item.show()
               box_item = DraggableLabel()  

           #for box_item in Sboxes_items:    
               #DraggableLabel(box_item)  

        else:
           self.checkBox_Short.setEnabled(True)
           for box_item in boxes_items:
               box_item.hide()

#==========================================================================

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())

I hope I can find someone who can help me with that. Thanks in advance.

Upvotes: 1

Views: 544

Answers (1)

musicamante
musicamante

Reputation: 48384

I believe you are a bit confused about how drag and drop works, but the most important thing is that you don't need "actual" drag and drop for what you want to achieve.

In this example I added the draggable boxes instead of the labels, and they are free to be moved within the image rectangle. I had to do some modifications to your original code, because it was a bit confusing and had some logic that simply didn't work.

class DraggableLabel(QLabel):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setStyleSheet("border-color: rgb(238, 0, 0); border-width : 2.0px; border-style:inset; background: transparent;")
        self.origin = None

    def setLimits(self, rect):
        self.limits = rect

    def mousePressEvent(self, event):
        if not self.origin:
            # update the origin point, we'll need that later
            self.origin = self.pos()
        if event.button() == Qt.LeftButton:
            self.mousePos = event.pos()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            # move the box
            self.move(self.pos() + event.pos() - self.mousePos)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            # if the box is not within the image boundaries, move it
            # back to the original position
            if not self.limits.contains(self.geometry()):
                self.move(self.origin)


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.setEnabled(True)
        Dialog.resize(1050, 800)
        Dialog.setMinimumSize(QtCore.QSize(1050, 800))
        Dialog.setMaximumSize(QtCore.QSize(1050, 800))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("../.designer/backup/project pic/images LPR icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        Dialog.setWindowIcon(icon)
        Dialog.setStyleSheet("background-color: rgb(217, 217, 217);\n"
"background-color: rgb(243, 243, 243);")
       # Dialog.setAcceptDrops(True)

        self.BrowesImageButton = QtWidgets.QPushButton(Dialog)
        self.BrowesImageButton.setGeometry(QtCore.QRect(820, 60, 200, 60))
        self.BrowesImageButton.setMinimumSize(QtCore.QSize(200, 60))
        self.BrowesImageButton.setMaximumSize(QtCore.QSize(200, 60))
        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei UI")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)

        self.BrowesImageButton.setFont(font)
        self.BrowesImageButton.setStyleSheet("")
        self.BrowesImageButton.setCheckable(True)
        self.BrowesImageButton.setObjectName("BrowesImageButton")
        self.groupBox = QtWidgets.QGroupBox(Dialog)
        self.groupBox.setGeometry(QtCore.QRect(820, 170, 211, 131))
        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei")
        font.setPointSize(10)
        font.setBold(True)

        font.setWeight(75)
        self.groupBox.setFont(font)
        self.groupBox.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.groupBox.setObjectName("groupBox")
        self.checkBox_Short = QtWidgets.QCheckBox(self.groupBox)
        self.checkBox_Short.setGeometry(QtCore.QRect(20, 40, 141, 23))
        font = QtGui.QFont()
        font.setFamily("Adobe Heiti Std R")
        font.setPointSize(10)

        self.checkBox_Short.setFont(font)
        self.checkBox_Short.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.checkBox_Short.setObjectName("checkBox_Short")
        self.checkBox_Short.setEnabled(False)

        self.checkBox_Long = QtWidgets.QCheckBox(self.groupBox)
        self.checkBox_Long.setGeometry(QtCore.QRect(20, 80, 141, 23))
        font = QtGui.QFont()
        font.setFamily("Adobe Heiti Std R")
        font.setPointSize(10)

        self.checkBox_Long.setFont(font)
        self.checkBox_Long.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.checkBox_Long.setObjectName("checkBox_Long")
        self.checkBox_Long.setEnabled(False)

        self.SegmentImageButton = QtWidgets.QPushButton(Dialog)
        self.SegmentImageButton.setGeometry(QtCore.QRect(830, 330, 200, 60))
        self.SegmentImageButton.setMinimumSize(QtCore.QSize(200, 60))
        self.SegmentImageButton.setMaximumSize(QtCore.QSize(200, 60))

        font = QtGui.QFont()
        font.setFamily("Microsoft YaHei UI")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.SegmentImageButton.setFont(font)
        self.SegmentImageButton.setStyleSheet("")
        self.SegmentImageButton.setCheckable(True)
        self.SegmentImageButton.setObjectName("SegmentImageButton")
        self.SegmentImageButton.setEnabled(False)

        self.UserImageLbl = QtWidgets.QLabel(Dialog)
        self.UserImageLbl.setGeometry(QtCore.QRect(40, 40, 750, 500))
        self.UserImageLbl.setMinimumSize(QtCore.QSize(750, 500))
        self.UserImageLbl.setMaximumSize(QtCore.QSize(750, 500))
        self.UserImageLbl.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.UserImageLbl.setFrameShadow(QtWidgets.QFrame.Plain)
        self.UserImageLbl.setLineWidth(1)
        self.UserImageLbl.setMidLineWidth(0)
        self.UserImageLbl.setText("")
        self.UserImageLbl.setScaledContents(False)
        self.UserImageLbl.setAlignment(QtCore.Qt.AlignCenter)
        self.UserImageLbl.setObjectName("UserImageLbl")

        self.buttonGroup = QButtonGroup()
        self.buttonGroup.addButton(self.checkBox_Short)
        self.buttonGroup.addButton(self.checkBox_Long)

        self.label_1 = DraggableLabel(Dialog)
        self.label_2 = DraggableLabel(Dialog)
        self.label_3 = DraggableLabel(Dialog)

        self.label_1.move(820, 470)
        self.label_2.move(900, 470)
        self.label_3.move(980, 470)

        self.labels = [self.label_1, self.label_2, self.label_3]
        for label in self.labels:
            label.hide()
            label.raise_()

        self.BrowesImageButton.clicked.connect(self.setImage)
        self.checkBox_Long.toggled.connect(self.setBoxSizes)
        self.checkBox_Short.toggled.connect(self.setBoxSizes)

    def setImage(self):
        fileName, _ = QtWidgets.QFileDialog.getOpenFileName(None, "Select Image", "", "Image Files (*.png *.jpg *jpeg *.bmp)") # Ask for file

        if fileName: # If the user gives a file
            pixmap = QtGui.QPixmap(fileName) # Setup pixmap with the provided image
            pixmap = pixmap.scaled(self.UserImageLbl.width(), self.UserImageLbl.height(), QtCore.Qt.KeepAspectRatio) # Scale pixmap
            self.UserImageLbl.setPixmap(pixmap) # Set the pixmap onto the label
            self.UserImageLbl.setAlignment(QtCore.Qt.AlignCenter) # Align the label to center
            #UserImageLbl = DropLabel()
            self.checkBox_Short.setEnabled(True)
            self.checkBox_Long.setEnabled(True)

    def setBoxSizes(self):
        if self.checkBox_Short.isChecked():
            boxSize = 40
        else:
            boxSize = 45

        for label in self.labels:
            label.setFixedSize(boxSize, boxSize)
            label.setLimits(self.UserImageLbl.geometry())
            label.show()

Upvotes: 1

Related Questions