Adrián
Adrián

Reputation: 21

Move(dragg) a QLabel inside a QFrame

I need to be able to show controls in the screen on top of a background image. I've already acomplished that task, and already created and displayed one control (a QLabel displaying an image). Now I need to be capable to move the label by just dragging it to where I wish to move it, i've followed several tutorials about dragging in pyqt, but i've failed accomplish this task

Here is my code. Note that the control can be moved, but when you move it, the background is moved too, and when you drop it, it stays in the same original position. What I want is to move only the control (the QLabel displaying the image) and move it just dragging inside the tab:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class CentralWidget(QFrame):
    def __init__(self, *args):
    super(CentralWidget, self).__init__(*args)
    self.setStyleSheet("background-image: url(logo.png);")
    self.setAcceptDrops(True)

    def dragEnterEvent(self, e):
        e.accept()

    def dropEvent(self, e):
        # get the relative position from the mime data
        mime = e.mimeData().text()
        x, y = map(int, mime.split(','))
        # move
        # so move the dragged button (i.e. event.source())
        e.source().move(e.pos()-QPoint(x, y))
        # set the drop action as Move
        e.setDropAction(Qt.MoveAction)
        # tell the QDrag we accepted it
        e.accept()

class Selector(QLabel):
    def mouseMoveEvent(self, e):
        if e.buttons() != Qt.LeftButton:
            return

        # write the relative cursor position to mime data
        mimeData = QMimeData()
        # simple string with 'x,y'
        mimeData.setText('%d,%d' % (e.x(), e.y()))

        # let's make it fancy. we'll show a "ghost" of the button as we drag
        # grab the button to a pixmap
        pixmap = QPixmap.grabWidget(self)

        # below makes the pixmap half transparent
        painter = QPainter(pixmap)
        painter.setCompositionMode(painter.CompositionMode_DestinationIn)
        painter.fillRect(pixmap.rect(), QColor(0, 0, 0, 127))
        painter.end()

        # make a QDrag
        drag = QDrag(self)
        # put our MimeData
        drag.setMimeData(mimeData)
        # set its Pixmap
        drag.setPixmap(pixmap)
        # shift the Pixmap so that it coincides with the cursor position
        drag.setHotSpot(e.pos())

        # start the drag operation
        # exec_ will return the accepted action from dropEvent
        if drag.exec_(Qt.MoveAction) == Qt.MoveAction:
            print 'moved'
        # else:
            # print 'copied'

    def mousePressEvent(self, e):
        QLabel.mousePressEvent(self,e)
        if e.button() == Qt.LeftButton:
            print 'press'


class fPrincipal(QMainWindow):
    def __init__(self, parent=None):
    # Call base class constructor
    QMainWindow.__init__(self, parent)

    self.setGeometry(QRect(0, 0, 599+10, 399+10))
    self.move(QDesktopWidget().availableGeometry().center() - self.frameGeometry().center())

    # Creamos el contenedor central, que sera organizado por pestañas
    centralWidget = QTabWidget()
    self.setCentralWidget(centralWidget);

    # Creamos la 1ra pestaña
    tab = CentralWidget()

    tabLayout = QHBoxLayout()
    tab.setLayout(tabLayout)

    # Añadimos la pestaña al contenedor central
    centralWidget.addTab(tab,"Escena 1")

    logDockWidget = QDockWidget("Tools", self)
    logDockWidget.setObjectName("LogDockWidget")
    logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
    self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)

    def crearMenu():
        mimenu = self.menuBar().addMenu("&Archivo")

    crearMenu()
    selectorLb = Selector()
    picture = QPixmap('D:\Adrian\Tesis\Codigo\selector.png')
    selectorLb.setPixmap(picture)
    tabLayout.addWidget(selectorLb)     

if __name__ == "__main__":
    app = QApplication(sys.argv)

    w = fPrincipal()
    w.show()

    sys.exit(app.exec_())

Upvotes: 2

Views: 3030

Answers (1)

user1006989
user1006989

Reputation:

Checkout the PyQt fridgemagnets example, here is a simplified version:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import sip
sip.setapi('QString', 2)

from PyQt4 import QtCore, QtGui

myMimeType = 'application/MyWindow'

class MyLabel(QtGui.QLabel):
    def __init__(self, parent):
        super(MyLabel, self).__init__(parent)

        self.setStyleSheet("""
            background-color: black;
            color: white;
            font: bold;
            padding: 6px;
            border-width: 2px;
            border-style: solid;
            border-radius: 16px;
            border-color: white;
        """)

    def mousePressEvent(self, event):
        itemData   = QtCore.QByteArray()
        dataStream = QtCore.QDataStream(itemData, QtCore.QIODevice.WriteOnly)
        dataStream.writeString(self.text())
        dataStream << QtCore.QPoint(event.pos() - self.rect().topLeft())

        mimeData = QtCore.QMimeData()
        mimeData.setData(myMimeType, itemData)
        mimeData.setText(self.text())

        drag = QtGui.QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(event.pos() - self.rect().topLeft())

        self.hide()

        if drag.exec_(QtCore.Qt.MoveAction | QtCore.Qt.CopyAction, QtCore.Qt.CopyAction) == QtCore.Qt.MoveAction:
            self.close()

        else:
            self.show()


class MyFrame(QtGui.QFrame):
    def __init__(self, parent=None):
        super(MyFrame, self).__init__(parent)

        self.setStyleSheet("""
            background-color: lightgray;
            border-width: 2px;
            border-style: solid;
            border-color: black;
            margin: 2px;
        """)

        y = 6
        for labelNumber in range(6):
            label = MyLabel(self)
            label.setText("Label #{0}".format(labelNumber))
            label.move(6, y)
            label.show()

            y += label.height() + 2

        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat(myMimeType):
            if event.source() in self.children():
                event.setDropAction(QtCore.Qt.MoveAction)
                event.accept()

            else:
                event.acceptProposedAction()

        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasFormat(myMimeType):
            mime       = event.mimeData()
            itemData   = mime.data(myMimeType)
            dataStream = QtCore.QDataStream(itemData, QtCore.QIODevice.ReadOnly)

            text = QtCore.QByteArray()
            offset = QtCore.QPoint()
            dataStream >> text >> offset

            newLabel = MyLabel(self)
            newLabel.setText(event.mimeData().text())
            newLabel.move(event.pos() - offset)
            newLabel.show()

            if event.source() in self.children():
                event.setDropAction(QtCore.Qt.MoveAction)
                event.accept()

            else:
                event.acceptProposedAction()

        else:
            event.ignore()

class MyWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)

        self.myFrame = MyFrame(self)

        self.setCentralWidget(self.myFrame)

if __name__ == "__main__":
    import sys

    app = QtGui.QApplication(sys.argv)
    app.setApplicationName('MyWindow')

    main = MyWindow()
    main.resize(333, 333)
    main.move(app.desktop().screen().rect().center() - main.rect().center())
    main.show()

    sys.exit(app.exec_())

image

Upvotes: 3

Related Questions