PyQt5 Grab and save section of screen - python

So the idea is that I should be able to fire up the script, select a portion of the screen, then hit enter (or otherwise trigger it) to save the selection.
I've got a good bit of the code from other posts and things, but now I'm stuck. I can select any portion of the screen and resize as needed, but I can't seem to get it to recognize the "enter" key. Right now the "keyPressEvent" function is just supposed to print a message so I know it worked, but I got nothing. Any ideas?
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QPoint, QRect, QSize
from PyQt5.QtGui import QScreen
from PyQt5.QtWidgets import QApplication, QLabel, QRubberBand
class MyLabel(QtWidgets.QLabel):
def __init__(self, parent=None):
QtWidgets.QLabel.__init__(self, parent)
self.selection = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
def keyPressEvent(self, qKeyEvent):
print(qKeyEvent.key())
if qKeyEvent.key() == QtCore.Qt.Key_Return:
print('Enter pressed')
else:
super().keyPressEvent(qKeyEvent)
def mousePressEvent(self, event):
'''
Mouse is pressed. If selection is visible either set dragging mode (if close to border) or hide selection.
If selection is not visible make it visible and start at this point.
'''
if event.button() == QtCore.Qt.LeftButton:
position = QtCore.QPoint(event.pos())
if self.selection.isVisible():
# visible selection
if (self.upper_left - position).manhattanLength() < 20:
# close to upper left corner, drag it
self.mode = "drag_upper_left"
elif (self.lower_right - position).manhattanLength() < 20:
# close to lower right corner, drag it
self.mode = "drag_lower_right"
else:
# clicked somewhere else, hide selection
self.selection.hide()
else:
# no visible selection, start new selection
self.upper_left = position
self.lower_right = position
self.mode = "drag_lower_right"
self.selection.show()
def mouseMoveEvent(self, event):
'''
Mouse moved. If selection is visible, drag it according to drag mode.
'''
if self.selection.isVisible():
# visible selection
if self.mode is "drag_lower_right":
self.lower_right = QtCore.QPoint(event.pos())
elif self.mode is "drag_upper_left":
self.upper_left = QtCore.QPoint(event.pos())
# update geometry
self.selection.setGeometry(QtCore.QRect(self.upper_left, self.lower_right).normalized())
class mainUI(QtWidgets.QWidget):
def __init__(self):
super(mainUI, self).__init__()
self.initUI()
def initUI(self):
layout = QtWidgets.QVBoxLayout(self)
label = MyLabel(self)
pixmap = QScreen.grabWindow(app.primaryScreen(), app.desktop().winId())
label.setPixmap(pixmap)
layout.addWidget(label)
self.setLayout(layout)
geometry = app.desktop().availableGeometry()
self.setFixedSize(geometry.width(), geometry.height())
# self.setWindowFlags( self.windowFlags() | Qt.FramelessWindowHint)
self.show()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = mainUI()
sys.exit(app.exec_())
EDIT
Alright this code works, although to be honest I'm not 100% sure why.
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QPoint, QRect, QSize
from PyQt5.QtGui import QScreen
from PyQt5.QtWidgets import QApplication, QLabel, QRubberBand, QAction
class KpeWindow(QtWidgets.QLabel):
def __init__(self, parent=None):
QtWidgets.QLabel.__init__(self,parent)
main = QtWidgets.QVBoxLayout(self)
self.selection = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
# label = QLabel(self)
# label.setText('Test the keyPressEvent')
# main.addWidget(label)
# self.adjustSize()
# self.setLayout(main)
def keyPressEvent(self, event):
print(event.key())
if event.key() == QtCore.Qt.Key_Return:
print('yay')
#QtWidgets.QMessageBox.warning(self, 'MDI', 'keyPressEvent')
self.parent().keyPressEvent(event)
def mousePressEvent(self, event):
'''
Mouse is pressed. If selection is visible either set dragging mode (if close to border) or hide selection.
If selection is not visible make it visible and start at this point.
'''
print(event)
if event.button() == QtCore.Qt.LeftButton:
position = QtCore.QPoint(event.pos())
if self.selection.isVisible():
# visible selection
if (self.upper_left - position).manhattanLength() < 20:
# close to upper left corner, drag it
self.mode = "drag_upper_left"
elif (self.lower_right - position).manhattanLength() < 20:
# close to lower right corner, drag it
self.mode = "drag_lower_right"
else:
# clicked somewhere else, hide selection
self.selection.hide()
else:
# no visible selection, start new selection
self.upper_left = position
self.lower_right = position
self.mode = "drag_lower_right"
self.selection.show()
def mouseMoveEvent(self, event):
'''
Mouse moved. If selection is visible, drag it according to drag mode.
'''
if self.selection.isVisible():
# visible selection
if self.mode is "drag_lower_right":
self.lower_right = QtCore.QPoint(event.pos())
elif self.mode is "drag_upper_left":
self.upper_left = QtCore.QPoint(event.pos())
# update geometry
self.selection.setGeometry(QtCore.QRect(self.upper_left, self.lower_right).normalized())
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
QtWidgets.QMainWindow.__init__(self)
#self.setWindowTitle('KeyPressEvent Test')
# main = QtWidgets.QVBoxLayout(self)
# child = KpeWindow(self)
# child.setFocusPolicy(Qt.StrongFocus)
# self.setFocusProxy(child)
# main.addWidget(child)
# child.setFocus(True)
layout = QtWidgets.QVBoxLayout(self)
label = KpeWindow(self)
pixmap = QScreen.grabWindow(app.primaryScreen(), app.desktop().winId())
label.setPixmap(pixmap)
layout.addWidget(label)
#new
label.setFocusPolicy(Qt.StrongFocus)
self.setFocusProxy(label)
label.setFocus(True)
self.setLayout(layout)
geometry = app.desktop().availableGeometry()
self.setFixedSize(geometry.width(), geometry.height())
# self.setWindowFlags( self.windowFlags() | Qt.FramelessWindowHint)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())

You need to set the focus policy on the label to get keyboard events:
class MyLabel(QtWidgets.QLabel):
def __init__(self, parent=None):
...
self.setFocusPolicy(Qt.TabFocus)

Related

Qt insert QGraphicsItem in scene on click position

I am designing an app to draw electrical circuit models and I need to insert elements on scene by clicking on a button and after on the scene.
The way it should work is the following: I press the button to insert a node, then it shows a message saying "Press where you want to insert the element", you press, and the element appear on screen.
I think the problem is that I have to stop the code to get the position and then continue or something like that.
Below I show a part of the code (the original contains more classes and a second tab for calculations, that it is not needed for this trouble and it is not connected in any way):
from PyQt5 import sip
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import QRectF, Qt, QSize, QPointF, QPoint, QLineF, showbase
from PIL import Image
import calculation
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Nodal Equations")
self.setWindowIcon(QIcon("images/flash.ico"))
self.setGeometry(150,50,1650,950)
self.setFixedSize(self.size())
self.UI()
self.show()
def UI(self):
self.toolBar()
self.tabwidgets()
self.widgets()
self.layouts()
def toolBar(self):
self.tb = QToolBar("Toolbar")
self.addToolBar(Qt.RightToolBarArea, self.tb)
self.tb.setIconSize(QSize(50,50))
# Toolbar buttons
self.addNode = QAction(QIcon("images/node.png"),"Node",self)
self.tb.addAction(self.addNode)
self.addNode.triggered.connect(self.funcAddNode)
self.tb.addSeparator()
def tabwidgets(self):
self.tabs = QTabWidget()
self.tabs.blockSignals(True) # To update tabs. After defining every layout we have to set it to False
self.setCentralWidget(self.tabs) # If this is not used, we cannot see the widgets (Needed if we use QMainWindow)
self.modelViewTab = QWidget()
self.tabs.addTab(self.modelViewTab, "Model View")
def widgets(self):
# Model View widgets
self.view = ModelView()
self.instructionsLabel = QLabel("Welcome to Node Equations Solver. To start select an order on the toolbar menu", self)
def layouts(self):
# Model View Tab layouts
self.mainModelLayout = QVBoxLayout()
self.sceneLayout = QHBoxLayout()
self.instructionsLayout = QHBoxLayout()
self.mainModelLayout.setAlignment(Qt.AlignCenter)
##### Adding widgets
self.sceneLayout.addWidget(self.view)
self.instructionsLayout.addWidget(self.instructionsLabel)
##### Adding layouts
self.mainModelLayout.addLayout(self.sceneLayout)
self.mainModelLayout.addLayout(self.instructionsLayout)
self.modelViewTab.setLayout(self.mainModelLayout)
def funcAddNode(self):
self.node = Node(0,500,500)
self.view.scene.addItem(self.node)
class Node(QGraphicsEllipseItem):
def __init__(self,number, x, y):
super(Node, self).__init__(0, 0, 10, 10)
self.number = number
self.setPos(x, y)
self.setBrush(Qt.yellow)
self.setAcceptHoverEvents(True)
# Mouse hover events
def hoverEnterEvent(self, event):
app.instance().setOverrideCursor(Qt.OpenHandCursor)
def hoverLeaveEvent(self, event):
app.instance().restoreOverrideCursor()
# Mouse click events
def mousePressEvent(self, event):
app.instance().setOverrideCursor(Qt.ClosedHandCursor)
def mouseReleaseEvent(self, event):
app.instance().restoreOverrideCursor()
def mouseMoveEvent(self, event):
orig_cursor_position = event.lastScenePos()
updated_cursor_position = event.scenePos()
orig_position = self.scenePos()
updated_cursor_x = updated_cursor_position.x() - orig_cursor_position.x() + orig_position.x()
updated_cursor_y = updated_cursor_position.y() - orig_cursor_position.y() + orig_position.y()
if updated_cursor_x < 0:
self.setPos(QPointF(0, updated_cursor_y))
elif updated_cursor_y < 0:
self.setPos(QPointF(updated_cursor_x, 0))
elif updated_cursor_x + self.boundingRect().right() > 1550:
self.setPos(QPointF(1550 - self.boundingRect().width(), updated_cursor_y))
elif updated_cursor_y + self.boundingRect().bottom() > 840:
self.setPos(QPointF(updated_cursor_x, 840 - self.boundingRect().height()))
else:
self.setPos(QPointF(updated_cursor_x, updated_cursor_y))
class ModelView(QGraphicsView):
def __init__(self):
super().__init__()
self.setRenderHints(QPainter.Antialiasing)
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.setSceneRect(0, 0, 1550, 840)
##### This is a way I tried to pick the cursor position and store it, but it didn't work
# def mousePressEvent(self, event):
# x = event.scenePos().x()
# y = event.scenePos().y()
# return (x,y)
def main():
global app
app = QApplication(sys.argv)
window = Main()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
You need to change the return (x,y) to something useful, i.e. emitting a signal or actually adding an element.
mousePressEvent is a method that does not return anything (void in C++).

Adding animation to QPushbutton enterEvent and exitEvent

I'm trying to add custom animation to QPushbutton without making a custom QPushbutton and overriding its enterEvent() and leaveEvent().
So far I've tried this,
#staticmethod
def addButtonHoverAnimation(button:QPushButton,currentPos:QPoint):
'''
Method to:
=> Add hover animation for provided button
'''
enterShift = QPropertyAnimation(button,b'pos',button)
exitShift = QPropertyAnimation(button,b'pos',button)
def enterEvent(e):
pos=button.pos()
enterShift.setStartValue(pos)
enterShift.setEndValue(QPoint(pos.x()+3,pos.y()+3))
enterShift.setDuration(100)
enterShift.start()
Effects.dropShadow(button,1,2)
def leaveEvent(e):
pos=button.pos()
exitShift.setStartValue(pos)
exitShift.setEndValue(QPoint(pos.x()-3,pos.y()-3))
exitShift.setDuration(100)
exitShift.start()
Effects.dropShadow(button)
button.enterEvent=enterEvent
button.leaveEvent=leaveEvent
But when I move the mouse very quickly in and out of the button before the animation finishes, The button starts to move wierdly towards the North-West direction.
Button Animation Using Dynamic Positions
I figured out this was due to the leaveEvent() being triggered before enterEvent() even finishes and also because the start and end values are dynamic. So, I tried providing currentPos as a static position and using it instead,
#staticmethod
def addButtonHoverAnimation(button:QPushButton,currentPos:QPoint):
'''
Method to:
=> Add hover animation for provided button
'''
enterShift = QPropertyAnimation(button,b'pos',button)
enterShift.setStartValue(currentPos)
enterShift.setEndValue(QPoint(currentPos.x()+3,currentPos.y()+3))
enterShift.setDuration(100)
exitShift = QPropertyAnimation(button,b'pos',button)
exitShift.setStartValue(QPoint(currentPos.x()-3,currentPos.y()-3))
exitShift.setEndValue(currentPos)
exitShift.setDuration(100)
def enterEvent(e):
button.setProperty(b'pos',exitShift.endValue())
enterShift.start()
Effects.dropShadow(button,1,2)
def leaveEvent(e):
exitShift.start()
Effects.dropShadow(button)
button.enterEvent=enterEvent
button.leaveEvent=leaveEvent
On running, as soon as the mouse enters the QPushbutton, it moves to the top-left of its parent widget and the animation starts working fine. I can't figure out why this is happening. But I was able to get that, it only happened when I used any static value in the animation.
Button Animation with Static Position:
Here is an example:
import sys
from PyQt5.QtCore import QEvent, QPoint, QObject, QPropertyAnimation
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
# This is the same method mentioned above
from styling import addButtonHoverAnimation
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout=QVBoxLayout()
button1 = QPushButton("Proceed1", self)
layout.addWidget(button1)
button2 = QPushButton("Proceed2", self)
layout.addWidget(button2)
self.setLayout(layout)
self.resize(640, 480)
addButtonHoverAnimation(button1)
addButtonHoverAnimation(button2)
def main():
app = QApplication(sys.argv)
view = Widget()
view.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
The problem is that probably when the state is changed from enter to leave (or vice versa) the previous animation still does not end so the position of the widget is not the initial or final position, so when starting the new animation there is a deviation that accumulates. One possible solution is to initialize the position and keep it as a reference.
On the other hand you should not do x.fooMethod = foo_callable since many can fail, in this case it is better to use an eventfilter.
import sys
from dataclasses import dataclass
from functools import cached_property
from PyQt5.QtCore import QEvent, QPoint, QObject, QPropertyAnimation
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
#dataclass
class AnimationManager(QObject):
widget: QWidget
delta: QPoint = QPoint(3, 3)
duration: int = 100
def __post_init__(self):
super().__init__(self.widget)
self._start_value = QPoint()
self._end_value = QPoint()
self.widget.installEventFilter(self)
self.animation.setTargetObject(self.widget)
self.animation.setPropertyName(b"pos")
self.reset()
def reset(self):
self._start_value = self.widget.pos()
self._end_value = self._start_value + self.delta
self.animation.setDuration(self.duration)
#cached_property
def animation(self):
return QPropertyAnimation(self)
def eventFilter(self, obj, event):
if obj is self.widget:
if event.type() == QEvent.Enter:
self.start_enter_animation()
elif event.type() == QEvent.Leave:
self.start_leave_animation()
return super().eventFilter(obj, event)
def start_enter_animation(self):
self.animation.stop()
self.animation.setStartValue(self.widget.pos())
self.animation.setEndValue(self._end_value)
self.animation.start()
def start_leave_animation(self):
self.animation.stop()
self.animation.setStartValue(self.widget.pos())
self.animation.setEndValue(self._start_value)
self.animation.start()
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button1 = QPushButton("Proceed1", self)
button1.move(100, 100)
button2 = QPushButton("Proceed2", self)
button2.move(200, 200)
self.resize(640, 480)
animation_manager1 = AnimationManager(widget=button1)
animation_manager2 = AnimationManager(widget=button2)
def main():
app = QApplication(sys.argv)
view = Widget()
view.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
184 / 5000
Resultados de traducción
If you are using a layout then you must reset the position since the layout does not apply the position change immediately but only when the parent widget applies the changes.
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button1 = QPushButton("Proceed1")
button2 = QPushButton("Proceed2")
lay = QVBoxLayout(self)
lay.addWidget(button1)
lay.addWidget(button2)
self.resize(640, 480)
self.animation_manager1 = AnimationManager(widget=button1)
self.animation_manager2 = AnimationManager(widget=button2)
def resizeEvent(self, event):
super().resizeEvent(event)
self.animation_manager1.reset()
self.animation_manager2.reset()

Getting mouse position doesn't work if button is clicked

I'm trying to get mouse position(in-window) whenever the left mouse button is pressed using:
def mousePressEvent(self, event):
print(event.pos())
however, any kind of interactive widget overrides it, for example- clicking a button doesn't print mouse position, same with line edit and others(maybe except label). How would I get the mouse position even if those widgets are clicked?
Explanation:
In the case of QWidgets, you have to take into account:
The transmission of the mouse event is from the top widget(child) to the bottom widget(parent).
If an element consumes the mouse event (ie use event.accept()) then that event will no longer be transmitted.
Solution:
If you want to detect the mouse event on a window then you could monitor that event on the window itself (QWindow) through an eventfilter:
import sys
from PyQt5.QtCore import pyqtSignal, QEvent, QObject, QPoint
from PyQt5.QtWidgets import (
QApplication,
QLineEdit,
QPushButton,
QTextEdit,
QVBoxLayout,
QWidget,
)
class MouseHelper(QObject):
pressed = pyqtSignal(QPoint)
released = pyqtSignal(QPoint)
def __init__(self, window):
super().__init__(window)
self._window = window
self.window.installEventFilter(self)
#property
def window(self):
return self._window
def eventFilter(self, obj, event):
if self.window is obj:
if event.type() == QEvent.MouseButtonPress:
self.pressed.emit(event.pos())
elif event.type() == QEvent.MouseButtonRelease:
self.released.emit(event.pos())
return super().eventFilter(obj, event)
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QPushButton("Press me")
lineedit = QLineEdit()
textedit = QTextEdit()
lay = QVBoxLayout(self)
lay.addWidget(button)
lay.addWidget(lineedit)
lay.addWidget(textedit)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Widget()
w.show()
helper = MouseHelper(w.windowHandle())
helper.pressed.connect(lambda point: print("pressed:", point))
helper.released.connect(lambda point: print("released:", point))
sys.exit(app.exec_())

Mouseover Event in PyQt5

I want to get the position of mouse while it's hovering over a label. I read this but my problem is different. I need to grab the mouse position as it hovers over my label without clicking so, mouseMoveEvent doesn't help
here's my code:
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.WindowGUI()
self.level = "Image Not Loaded Yet"
self.mouseIsClicked = False
self.top = 90
self.left = 90
self.height = 1800
self.width = 1800
self.setGeometry(self.top, self.left, self.height, self.width)
self.setWindowTitle("Manual Contact Andgle")
self.setMouseTracking(True)
mainWidget = QWidget(self)
self.setCentralWidget(mainWidget)
mainWidget.setLayout(self.finalVbox)
self.show()
def WindowGUI(self):
self.finalVbox = QVBoxLayout() # Final Layout
self.mainHBox = QHBoxLayout() # Hbox for picLable and Buttons
self.mainVBox = QVBoxLayout() # VBox for two Groupboxes
self.lineVBox = QVBoxLayout() # VBox For Line Drawing Buttons
self.fileVBox = QVBoxLayout() # VBox for file Loading and Saving Buttons
self.lineGroupbox = QGroupBox("Drawing") # GroupBox For Line Drawing Buttons
self.fileGroupbox = QGroupBox("File") # GroupBox for File Loading and Saving Buttons
self.picLable = Label(self) # Lable For showing the Image
self.piclable_pixmap = QPixmap("loadImage.png") # Setting Pixmap
self.picLable.setPixmap(self.piclable_pixmap) # setting pixmap to piclable
def mouseMoveEvent(self, QMouseEvent):
print(QMouseEvent.pos())
If you want to detect the mouse position without pressing on the widget then you must enable mouseTracking that will make the mouseMoveEvent invoked when the mouse is pressed or not, if you want to verify that it is not pressed you must use the buttons() method:
import sys
from PyQt5 import QtWidgets
class Label(QtWidgets.QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.setMouseTracking(True)
def mouseMoveEvent(self, event):
if not event.buttons():
print(event.pos())
super().mouseMoveEvent(event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Label()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
UPDATE:
Mouse events are propagated from children to parents if the children do not consume it, that is, if the child consumes it then the parent cannot consume it. So the QLabel is consuming that event so the window will not be notified, so in this case an eventFilter should be used:
import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QEvent, QObject, QPoint
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow
class HoverTracker(QObject):
positionChanged = pyqtSignal(QPoint)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.setMouseTracking(True)
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, obj, event):
if obj is self.widget and event.type() == QEvent.MouseMove:
self.positionChanged.emit(event.pos())
return super().eventFilter(obj, event)
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Manual Contact Andgle")
self.picLable = QLabel(self)
self.picLable.setPixmap(QPixmap("loadImage.png"))
hover_tracker = HoverTracker(self.picLable)
hover_tracker.positionChanged.connect(self.on_position_changed)
#pyqtSlot(QPoint)
def on_position_changed(self, p):
print(p)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
You can use enterEvent.
Enter event gets called everytime the mouse is over the widget. With the event.pos() you can get the mouse coordinates.
self.label.enterEvent = lambda event: print(event.pos())

PyQt5 - Drag and Drop in QDialog

I have a main pyqt5 application.
On click of a button i would like to call a QDialog and on submit of QDialog want to capture the result of QDialog in main application.
Below is a pyqt module which i like add it to QDialog, is this possible, and want to capture the result as well in main application ?
I don't know where to start.
import sys
from PyQt5.QtCore import QSize
from PyQt5 import QtWidgets, QtCore,QtGui
from PyQt5.QtGui import QColor, QDrag, QPainter, QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtWidgets
from win32api import GetSystemMetrics
from PyQt5.QtGui import QImage, QPalette, QBrush
w=GetSystemMetrics(0)/1366
h=GetSystemMetrics(1)/768
class Button(QtWidgets.QPushButton):
def mouseMoveEvent(self, e):
if e.buttons() != QtCore.Qt.LeftButton:
return
# write the relative cursor position to mime data
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
pixmap = QtWidgets.QWidget.grab(self)
# below makes the pixmap half transparent
painter = QtGui.QPainter(pixmap)
painter.setCompositionMode(painter.CompositionMode_DestinationIn)
painter.fillRect(pixmap.rect(), QtGui.QColor(0, 0, 0, 127))
painter.end()
# make a QDrag
drag = QtGui.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_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
print('moved')
else:
print('copied')
class Example(QtWidgets.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
button = Button('Draggable Text', self)
button.setStyleSheet("font-size: 15px;background-color: rgb(255,255,255); margin:5px; border:1px solid rgb(255, 255, 255); ")
button.move(100, 65)
self.buttons = [button]
self.setWindowTitle('Copy or Move')
self.setFixedSize(960, 515)
oImage = QImage(r"C:\Users\vaas\Desktop\background.jpg")
sImage = oImage.scaled(QSize(960*w/1,515*h/1)) # resize Image to widgets size
palette = QPalette()
palette.setBrush(QPalette.ColorRole(10), QBrush(sImage)) # 10 = Windowrole
self.setPalette(palette)
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(','))
if e.keyboardModifiers() & QtCore.Qt.ShiftModifier:
# copy
# so create a new button
button = Button('Button', self)
# move it to the position adjusted with the cursor position at drag
button.move(e.pos()-QtCore.QPoint(x, y))
# show it
button.show()
# store it
self.buttons.append(button)
# set the drop action as Copy
e.setDropAction(QtCore.Qt.CopyAction)
else:
# move
# so move the dragged button (i.e. event.source())
print('mine°',e.pos())
e.source().move(e.pos()-QtCore.QPoint(x, y))
# set the drop action as Move
e.setDropAction(QtCore.Qt.MoveAction)
# tell the QDrag we accepted it
e.accept()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Start with http://doc.qt.io/qt-5/dnd.html
Play with the example below:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSize
from PyQt5.QtGui import (QColor, QDrag, QPainter, QPixmap,
QImage, QPalette, QBrush)
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout
from win32api import GetSystemMetrics
w=GetSystemMetrics(0)/1366
h=GetSystemMetrics(1)/768
### +++++++++++++++++++++++++++++
class Window2(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# To be able to receive media dropped on a widget, call setAcceptDrops(true)
# for the widget, and reimplement the dragEnterEvent() and
# dropEvent() event handler functions.
self.setAcceptDrops(True)
self.setGeometry(QtCore.QRect(20, 50, 180, 600))
self.lay = QVBoxLayout()
self.lay.addWidget(QtWidgets.QPushButton('Button1'))
self.lay.addWidget(QtWidgets.QPushButton('Button2'))
self.lay.addWidget(QtWidgets.QPushButton('Button3'))
palette = self.palette()
palette.setColor(QPalette.Window, QColor('blue'))
self.setPalette(palette)
self.setLayout(self.lay)
self.textLabel = ''
# The dragEnterEvent() function is typically used to inform Qt about the types
# of data that the widget accepts. You must reimplement this function
# if you want to receive either QDragMoveEvent or QDropEvent
# in your reimplementations of dragMoveEvent() and dropEvent().
def dragEnterEvent(self, e):
e.accept()
# The dropEvent() is used to unpack dropped data and
# handle it in way that is suitable for your application.
def dropEvent(self, e):
#self.textLabel = 'Drag and Drop. QLabel. e.mimeData().text() -> `{}`'.format(e.mimeData().text())
self.lay.addWidget(QtWidgets.QLabel('Drag and Drop. QLabel. e.mimeData().text() -> `{}`'\
.format(e.mimeData().text())))
e.accept()
class Button(QtWidgets.QPushButton):
def mouseMoveEvent(self, e):
if e.buttons() != QtCore.Qt.LeftButton:
return
# write the relative cursor position to mime data
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
pixmap = QtWidgets.QWidget.grab(self)
# below makes the pixmap half transparent
painter = QtGui.QPainter(pixmap)
painter.setCompositionMode(painter.CompositionMode_DestinationIn)
painter.fillRect(pixmap.rect(), QtGui.QColor(0, 0, 0, 127))
painter.end()
# make a QDrag
drag = QtGui.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_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
print('moved')
else:
print('copied')
class Example(QtWidgets.QWidget):
def __init__(self):
super(Example, self).__init__()
self.w2 = Window2() # +++
self.w2.show() # +++
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
button = Button('Draggable Text', self)
button.setStyleSheet("font-size: 15px;background-color: rgb(255,255,255); margin:5px; border:1px solid rgb(255, 255, 255); ")
button.move(100, 65)
self.buttons = [button]
self.setWindowTitle('Copy or Move')
self.setFixedSize(960, 515)
oImage = QImage(r"C:\Users\vaas\Desktop\background.jpg")
#oImage = QImage(r"logo.png")
sImage = oImage.scaled(QSize(960*w/1,515*h/1)) # resize Image to widgets size
palette = QPalette()
palette.setBrush(QPalette.ColorRole(10), QBrush(sImage)) # 10 = Windowrole
self.setPalette(palette)
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(','))
if e.keyboardModifiers() & QtCore.Qt.ShiftModifier:
# copy
# so create a new button
button = Button('Button', self)
# move it to the position adjusted with the cursor position at drag
button.move(e.pos()-QtCore.QPoint(x, y))
# show it
button.show()
# store it
self.buttons.append(button)
# set the drop action as Copy
e.setDropAction(QtCore.Qt.CopyAction)
else:
# move
# so move the dragged button (i.e. event.source())
print('mine°',e.pos())
e.source().move(e.pos()-QtCore.QPoint(x, y))
# set the drop action as Move
e.setDropAction(QtCore.Qt.MoveAction)
# tell the QDrag we accepted it
e.accept()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories

Resources