how to translate or move QGraphicsView by mouse drag? - qt

what I did:
class PDFView(QGraphicsView):
def __init__
#somecode...
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
if self.itemAt(event.pos()) is None:
self.begin_drag=True
self.begin_drag_pos=event.pos()
super().mousePressEvent(event)
def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
if self.begin_drag and self.begin_drag_pos is not None:
delta = self.begin_drag_pos - event.pos()
old_center = QPointF(
(self.mapToScene(self.pos()).x() + self.width()) / 2,
(self.mapToScene(self.pos()).y() + self.height()) / 2
)
self.centerOn(old_center+delta)
self.update()
else:
super().mouseMoveEvent(event)
pass
def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
if self.begin_drag:
self.begin_drag=False
self.begin_drag_pos=None
super().mouseReleaseEvent(event)
what I get: when I dragup , the scroll bar goes down, it's ok, but it will go up when next time I dragup.
I tried: translate,move, all failed.
how to translate or move QGraphicsView by mouse drag?
what's the problem with my code?

Related

QDoubleSpinBox + MousePressEvent

I am trying to implement my own version of the QDoubleSpinBox to have it act like in Maya with int/FloatFields so you can Ctrl+click+drag to change the value.
I have this system working on a QLabel by setting the text from my value but then when I try to do it on a QDoubleSpinBox I have a problem with the mousePressEvent. It only works on the arrowButtons, not it the field itself...
here is my initial code :
class MayaLikeDoubleField(QtGui.QDoubleSpinBox):
def __init__(self, parent):
super(MayaLikeDoubleField, self).__init__()
self.offset = 0
def mousePressEvent(self, event):
self.offset = event.x()
def mouseMoveEvent(self, event):
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ControlModifier:
QtGui.QWidget.mouseMoveEvent(self, event)
relPos = event.x()-self.offset
print relPos*0.01
# instead of printing I would set the value.
In this case, it doesn' work. So I have tried this :
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
self.layout = QtGui.QHBoxLayout(self)
self.sbox = QtGui.QDoubleSpinBox()
self.layout.addWidget(self.line)
self.sbox.installEventFilter(self)
def mousePressEvent(self, event):
print "Main Widget Mouse Press"
super(Widget, self).mousePressEvent(event)
def eventFilter(self, obj, event):
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ControlModifier:
if self.layout.indexOf(obj) != -1:
if event.type() == event.MouseButtonPress:
print "Widget click"
if event.type() == event.MouseMove:
print "Widget Move"
return super(Widget, self).eventFilter(obj, event)
And surprise ! it doesn't work ... but if i replace the QDoubleSpinBox by a QLineEdit it does work ... why ? for me it works the same way as by default the mousePressButton doesn't work on it...
Is there anything special that I am missing ?
Thanks !

python3.4 + pyqt5: Getting x,y out of QMouseEvent

python3.4+pyqt5
I'm trying to write a program in which buttons shall create other buttons by dragging and press_releasing to form a representation of a rolling mill street.
I created a class Button which does the moving and handles several clicking events for the buttons in creation. This part is working fine.
Where I'm stuck is, I want to read the mouse position where these button should be created.
Here's the code:
def createConnects(self):
self.id000001.released.connect(self.do_something)
self.id000002.released.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
self.bname = 'Button'
self.button = Button(self.bname, self)
posx = QtGui.QMouseEvent.x()
print(posx)
posy = QtGui.QMouseEvent.y
print(posy)
sender = self.sender()
print(str(sender.objectName()))
#self.button.move(posx, posy)
qmouseevent provides several functions, but the posx line gives me this error:
TypeError: QMouseEvent.x(): first argument of unbound method must have type 'QMouseEvent'
posy-line gives < built-in function y > which isn't what I want, too, but clear.
MouseTracking is switched on in MainWindow class.
Maybe, normaly one would do that by using event in the def line but since it's a slot, that would lead to other problems.
Any hints to get along?
Update:
As recommended here's the full code of the prototype:
#!/usr/bin/env python3.4
import sys, os, math, shutil, re
from subprocess import call, Popen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.uic import loadUiType
Ui_MainWindow, QMainWindow = loadUiType('mainwindow.ui')
def main(argv):
app = QtWidgets.QApplication(argv)
mainwindow = MyMainWindow()
mainwindow.show()
sys.exit(app.exec_())
class Button(QtWidgets.QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
self.button = QtWidgets.QPushButton()
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.RightButton:
return
mimeData = QtCore.QMimeData()
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
dropAction = drag.exec_(QtCore.Qt.MoveAction)
def mousePressEvent(self, event):
QtWidgets.QPushButton.mousePressEvent(self, event)
if event.button() == QtCore.Qt.LeftButton:
print(event.button(),' pressed')
class MyMainWindow(QtWidgets.QMainWindow,Ui_MainWindow):
def __init__(self, *args):
QtWidgets.QMainWindow.__init__(self, *args)
self.setupUi(self)
self.setMouseTracking(True)
self.createConnects()
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
position = event.pos()
self.button.move(position)
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
def createConnects(self):
self.id000001.released.connect(self.do_something)
self.id000002.released.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print('do_something')
self.bname = 'Button'
self.button = QtWidgets.QPushButton()
#self.button = Button(self.bname, self)
self.button.move(100, 65)
#posx = QtGui.QMouseEvent.x()
#print(posx)
#posy = QtGui.QMouseEvent.y
#print(posy)
sender = self.sender()
print(str(sender.objectName()))
#self.button.move(posx, posy)
if __name__ == '__main__':
main(sys.argv)
cheers,
Christian

PyQt: eventFilter to get mouse position in a semi-transparent window

I want to make:
a semi-transparent fullscreen window (rgba(0,0,0,180)).
while moving mouse, display absolute position on label.
user can press on it to get the absolute position of the mouse.
However I cannot achieve the second one. When moving mouse on it, label won't update mouse's position. But I found when moving out of label (after removing layout.setMargin(0) and layout.setSpacing(0)), it works.
# -*- coding: utf-8 -*-
import sys, os, math
from PyQt4 import QtCore, QtGui
class ScreenPositionLabel(QtGui.QWidget):
def __init__(self):
super(ScreenPositionLabel, self).__init__()
self.setStyleSheet("background-color:rgba(0, 0, 0, 180); color:#fff;")
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
#self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, False)
#self.setStyleSheet("QMainWindow{opacity:0.5;}")
self.label = QtGui.QLabel("Please click on screen")
self.label.setAlignment(QtCore.Qt.AlignCenter)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.label)
# remove margin and padding
layout.setMargin(0)
layout.setSpacing(0)
self.setLayout(layout)
self.setMouseTracking(True)
self.installEventFilter(self)
self.label.show()
self.show()
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseMove and
event.buttons() == QtCore.Qt.NoButton):
pos = event.pos()
self.label.setText('Please click on screen. ( %d : %d )' % (pos.x(), pos.y()))
elif event.type() == QtCore.QEvent.MouseButtonPress:
pos = event.pos()
print('( %d : %d )' % (pos.x(), pos.y()))
self.close()
return QtGui.QWidget.eventFilter(self, source, event)
app = QtGui.QApplication(sys.argv)
main_window = ScreenPositionLabel()
app.exec_()
Any way to solve this problem? Thanks!
Your 4 questions:
1) I want to make: a semi-transparent fullscreen window (rgba(0,0,0,180)).
Yes, you can. Please use QWidget.setWindowOpacity (self, float level).
2) I want to make: while moving mouse, display absolute position on label.
I recommend using QWidget.mouseMoveEvent (self, QMouseEvent) to get current position your mouse and enable QWidget.setMouseTracking (self, bool enable) for track all mouse movement.
QWidget.setMouseTracking (self, bool enable)
QWidget.mouseMoveEvent (self, QMouseEvent)
3) I want to make: user can press on it to get the absolute position of the mouse.
Using QWidget.mousePressEvent (self, QMouseEvent) to track when mouse press.
4) However I cannot achieve the second one. When moving mouse on it, label won't update mouse's position. But I found when moving out of label (after removing layout.setMargin(0) and layout.setSpacing(0)), it works.
Because in default layout height of QLabel has spacing & margin, then real area isn't all area widget solve it is your solution is OK.
Full example for your solution:
import sys
from PyQt4 import QtGui, QtCore
class QCustomLabel (QtGui.QLabel):
def __init__ (self, parent = None):
super(QCustomLabel, self).__init__(parent)
self.setMouseTracking(True)
self.setTextLabelPosition(0, 0)
self.setAlignment(QtCore.Qt.AlignCenter)
def mouseMoveEvent (self, eventQMouseEvent):
self.setTextLabelPosition(eventQMouseEvent.x(), eventQMouseEvent.y())
QtGui.QWidget.mouseMoveEvent(self, eventQMouseEvent)
def mousePressEvent (self, eventQMouseEvent):
if eventQMouseEvent.button() == QtCore.Qt.LeftButton:
QtGui.QMessageBox.information(self, 'Position', '( %d : %d )' % (self.x, self.y))
QtGui.QWidget.mousePressEvent(self, eventQMouseEvent)
def setTextLabelPosition (self, x, y):
self.x, self.y = x, y
self.setText('Please click on screen ( %d : %d )' % (self.x, self.y))
class QCustomWidget (QtGui.QWidget):
def __init__ (self, parent = None):
super(QCustomWidget, self).__init__(parent)
self.setWindowOpacity(0.7)
# Init QLabel
self.positionQLabel = QCustomLabel(self)
# Init QLayout
layoutQHBoxLayout = QtGui.QHBoxLayout()
layoutQHBoxLayout.addWidget(self.positionQLabel)
layoutQHBoxLayout.setMargin(0)
layoutQHBoxLayout.setSpacing(0)
self.setLayout(layoutQHBoxLayout)
self.showFullScreen()
myQApplication = QtGui.QApplication(sys.argv)
myQTestWidget = QCustomWidget()
myQTestWidget.show()
myQApplication.exec_()

How to handle mouse events in Qt?

Dragging, panning and mouse tracking items in Qt scene are the 3 actions which I want to simultaneously use in my app. However, it is more difficult than I thought.
Example code :
from PySide.QtCore import *
from PySide.QtGui import *
class View(QGraphicsView):
"""# HOW TO ATTACH MOUSE EVENTS PROGRAMMATICALLY ?
def mouseMoveEvent(self, event):
print "View MOVE", event.pos()
"""
class Scene(QGraphicsScene):
pass
class CircleForTrackingSwitch(QGraphicsEllipseItem):
def __init__(self, scene):
super(CircleForTrackingSwitch, self).__init__()
self.scene = scene
self.view = self.scene.views()[0]
self.setRect(QRect(20,20,20,20))
self.scene.addItem(self)
def mousePressEvent(self, event):
if self.view.hasMouseTracking() :
self.view.setMouseTracking(False)
else :
self.view.setMouseTracking(True)
print "Circle View SetTrack", event.pos()
def mouseMoveEvent(self, event):
print "Circle MOVE", event.pos()
class DraggableRectangle(QGraphicsRectItem):
def __init__(self, scene):
super(DraggableRectangle, self).__init__()
self.scene = scene
self.setRect(QRect(-20,-20,40,40))
self.scene.addItem(self)
#self.setFlag(QGraphicsItem.ItemIsMovable, True)
def mousePressEvent(self, event):
print "Rectangle PRESS", event.pos()
def mouseMoveEvent(self, event):
print "Rectangle MOVE", event.pos()
self.setPos(event.pos())
def mouseReleaseEvent(self, event):
print "Rectangle RELEASE", event.pos()
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.s = Scene()
self.s.setSceneRect(-200,-100,300,300,)
self.v = View(self.s)
self.v.setDragMode(QGraphicsView.ScrollHandDrag)
self.setCentralWidget(self.v)
CircleForTrackingSwitch(self.s)
DraggableRectangle(self.s)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.resize(300, 200)
window.show()
sys.exit(app.exec_())
MASTER QUESTION :
How to attach mouse events programmatically? Many thanks.
For the draggable Rectangle, since the Rectangle is already moveable, you only have to subclass it, to overload the different mouse functions (with the track stuff), then to call the parent's mouse event. Basically, you could have what you want with this rectangle class:
class DraggableRectangle(QGraphicsRectItem):
def __init__(self, scene, *args, **kwargs):
super().__init__(*args, **kwargs)
self.scene = scene
self.setRect(QRect(-20,-20,40,40))
self.scene.addItem(self)
self.setFlag(QGraphicsItem.ItemIsMovable, True) # Keep that
def mousePressEvent(self, event):
print "Rectangle PRESS", event.pos()
super().mousePressEvent(event) # Call parent
def mouseMoveEvent(self, event):
print "Rectangle MOVE", event.pos()
# Do not move by yourself
# Call parent that already handles the move
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
print "Rectangle RELEASE", event.pos()
super().mouseReleaseEvent(event) # Call parent
I hope this is what you were looking for.

QGraphicsView double click events and ScrollHandDrag mode item issue

I'm attempting to create a QGraphicsView with the following behaviour:
When the control key is held and the left mouse is down, the view should be set to ScrollHandDrag mode to allow the user to pan around.
When in ScrollHandDrag mode, items should not be selectable/movable, as in the question here: In ScrollHandDrag mode of QGraphicsView, How to stop movement of QGraphicsItems on scene?
If the control key was held, the left mouse was clicked, and then the control key was released, then the view should stay in ScrollHandDrag mode until the mouse is released, or will stay in this mode should the control key be down at the time the mouse is released.
To me this sounds like it should be fairly straightforward. I've implemented the logic from the linked question, and some additional logic for my extra requirements. However this seems to cause the following two showstoppers:
In the mousePressEvent, setting the item under the mouse to not have the movable and selectable flags, calling the base class, and then re-applying the flags causes the item to become "frozen". The only way to solve this seems to be a control + click, release control + release click a few times outside of the item. Also when it gets into this state no items can be moved (although they can still be selected).
Double clicking the view causes a mousePressEvent, which is then followed by two mouseReleaseEvents! This breaks my logic.
So I would like to know how I can solve the issue of the items becoming frozen when the logic from In ScrollHandDrag mode of QGraphicsView, How to stop movement of QGraphicsItems on scene? is used, and how to deal with the strange double click mouse events - is there a way to turn them off?
Here is my code (this is also pretty much my hello world Python, so if I've made some horrible Python mistake please let me know):
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Test")
self.gv = MyGraphicsView()
self.setCentralWidget(self.gv)
self.setGeometry(170, 130, 450, 250)
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setup()
def setup(self):
self.m_MouseIsDown = False
self.m_ControlKeyDown = False
self.setDragMode(QGraphicsView.RubberBandDrag)
def mouseMoveEvent(self, event):
# print "mouseMoveEvent: " + str(event.pos().x()) + "," + str(event.pos().y())
super(MyGraphicsView, self).mouseMoveEvent(event);
def mousePressEvent(self, event):
print "mousePressEvent"
itemUnderMouse = self.itemAt(event.pos())
if itemUnderMouse is not None:
bHadMovableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsMovable
bWasSelected = itemUnderMouse.isSelected()
bHadSelectableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsSelectable
if bHadMovableFlagSet:
print "has ItemIsMovable"
else:
print "hasn't ItemIsMovable"
if bHadSelectableFlagSet:
print "has ItemIsSelectable"
else:
print "hasn't ItemIsSelectable"
if bWasSelected:
print "isSelected true"
else:
print "isSelected false"
itemUnderMouse.setSelected(False)
if event.button() == Qt.LeftButton:
print "mousePressEvent: left button is now down"
self.m_MouseIsDown = True
if self.dragMode() == QGraphicsView.ScrollHandDrag and event.button() == Qt.LeftButton:
print "mousePressEvent: left button down and ScrollHandDrag set"
self.PreventItemsFromMovingOrBeingSelectedWhenPannning(event)
return
print "mousePressEvent: pass through"
super(MyGraphicsView, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
print "mouseReleaseEvent"
if event.button() == Qt.LeftButton:
print "mouseReleaseEvent - left button is now up"
self.m_MouseIsDown = False
if self.dragMode() == QGraphicsView.ScrollHandDrag and self.m_ControlKeyDown == False:
print "mouseReleaseEvent - left button up, in ScrollHandDrag mode and control key is not pressed, change to RubberBandDrag"
self.setDragMode(QGraphicsView.RubberBandDrag)
super(MyGraphicsView, self).mouseReleaseEvent(event)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Control:
print "control key down"
self.m_ControlKeyDown = True
# ignore if mouse already down since we don't want to suddenly change to pan mode if an item is being moved
if event.key() == Qt.Key_Control and self.dragMode() != QGraphicsView.ScrollHandDrag and self.m_MouseIsDown == False:
print "keyPressEvent - control key down, mouse isn't down and drag mode is not ScrollHandDrag, change to ScrollHandDrag"
self.setDragMode(QGraphicsView.ScrollHandDrag)
super(MyGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Control:
print "control key up"
self.m_ControlKeyDown = False
if event.key() == Qt.Key_Control and self.dragMode() == QGraphicsView.ScrollHandDrag and self.m_MouseIsDown == False:
print "keyReleaseEvent - control key up and drag mode is ScrollHandDrag, mouse is not pressed, change to RubberBandDrag"
self.setDragMode(QGraphicsView.RubberBandDrag)
super(MyGraphicsView, self).keyReleaseEvent(event)
def wheelEvent(self, event):
factor = 1.2;
if event.delta() < 0:
factor = 1.0 / factor
self.scale(factor, factor)
def PreventItemsFromMovingOrBeingSelectedWhenPannning(self, mouseEvent):
itemUnderMouse = self.itemAt(mouseEvent.pos())
if itemUnderMouse is not None:
print "preventing item from moving"
bHadMovableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsMovable
itemUnderMouse.setFlag(QGraphicsItem.ItemIsMovable, False)
bWasSelected = itemUnderMouse.isSelected()
bHadSelectableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsSelectable
itemUnderMouse.setFlag(QGraphicsItem.ItemIsSelectable, False)
super(MyGraphicsView, self).mousePressEvent(mouseEvent)
if bHadMovableFlagSet:
print "set ItemIsMovable"
itemUnderMouse.setFlag(QGraphicsItem.ItemIsMovable, True)
if bHadSelectableFlagSet:
print "set ItemIsSelectable"
itemUnderMouse.setFlag(QGraphicsItem.ItemIsSelectable, True)
if bWasSelected:
print "setSelected True"
itemUnderMouse.setSelected(True)
else:
print "no item under mouse - pass through"
super(MyGraphicsView, self).mousePressEvent(mouseEvent)
class MyGraphicsScene(QGraphicsScene):
def __init__(self, parent):
super(MyGraphicsScene, self).__init__()
def main():
a = QApplication(sys.argv)
w = MyMainWindow()
w.show()
scene = MyGraphicsScene(w)
w.gv.setScene(scene)
rect = scene.addRect( 10, 10, 40, 40)
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
rect = scene.addRect( 40, 40, 40, 40)
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
sys.exit(a.exec_())
if __name__ == '__main__':
main()
If you don't call the base implementation in mouse*Events while panning, item selection won't be an issue. However, this now requires re-implementing the built-in panning function. Fortunately, it's not hard to implement it.
After some iteration at IRC (#pyqt # freenode), this is the final implementation:
Pressing and holding CTRL key enables panning. While mouse is pressed, releasing CTRL key keeps panning mode.
Just mouse press activates Rubber selection
All actions are controlled with Left-Button
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._mousePressed = True
if self._isPanning:
self.setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
else:
super(MyGraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._mousePressed and self._isPanning:
newPos = event.pos()
diff = newPos - self._dragPos
self._dragPos = newPos
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
event.accept()
else:
super(MyGraphicsView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if event.modifiers() & Qt.ControlModifier:
self.setCursor(Qt.OpenHandCursor)
else:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
self._mousePressed = False
super(MyGraphicsView, self).mouseReleaseEvent(event)
def mouseDoubleClickEvent(self, event): pass
def keyPressEvent(self, event):
if event.key() == Qt.Key_Control and not self._mousePressed:
self._isPanning = True
self.setCursor(Qt.OpenHandCursor)
else:
super(MyGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Control:
if not self._mousePressed:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
else:
super(MyGraphicsView, self).keyPressEvent(event)
def wheelEvent(self, event):
factor = 1.2;
if event.delta() < 0:
factor = 1.0 / factor
self.scale(factor, factor)

Resources