Implement dragMoveEvent on QWidget in pyqt5? - python

does anyone know how I can implement the dragMove event on my QWidget? So basically what I want is to move my mouse over the Widget hold down my mouse button and drag it. While dragging, the widget should not be moved it should only capture the mouse coordinates while the mouse is pressed.
I have already googled and just find some drag and drop tutorials where they have dragged something into a widget etc. like text. This wasn't really helpful.

This has got nothing to do with dragging. What you actually need to do is enable mouse-tracking and then monitor mouse-move events.
Here's a simple demo:
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.setMouseTracking(True)
def mouseMoveEvent(self, event):
if event.buttons() & QtCore.Qt.LeftButton:
print(event.globalPos().x(), event.globalPos().y())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 150, 100, 100)
window.show()
sys.exit(app.exec_())

I think you are looking for mousePressEvent rather than dragMoveEvent. You would need to subclass QWidget and implement the mousePressEvent method providing your implementation:
from PyQt5.QtWidgets import QWidget
class MyWidget(QWidget):
def mousePressEvent(self, event):
print(event.pos())

Related

identify an instance in drag-and-drop

I try hard to understand how to identify an instance of a class within drag and drop, but i need some help.
Beneath I add a sample from "http://zetcode.com/gui/pyqt5/dragdrop/" to explain.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
In this program, we can press on a button with a left mouse
click or drag and drop the button with the right mouse click.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
import sys
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
def mouseMoveEvent(self, e):
if e.buttons() != Qt.RightButton:
return
mimeData = QMimeData()
drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())
dropAction = drag.exec_(Qt.MoveAction)
def mousePressEvent(self, e):
super().mousePressEvent(e)
if e.button() == Qt.LeftButton:
print('press')
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.button = Button('Button', self)
self.button.move(100, 65)
self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 280, 150)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
position = e.pos()
self.button.move(position)
e.setDropAction(Qt.MoveAction)
e.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()
Here my questions:
if I instantiate a 2nd button
self.button2 = Button('Button2', self)
self.button2.move(100, 165)
i can also move the button2, but in the DropEvent the first button "button" is moved.
How is it possible to identify within DropEvent, that I move "button2" und place button2?
So: question is: how to identify, which button it is.
(i found only drag n drop examples, how to replace a label or a button by another element of same type)
And additional question: to create a new instance works with this type of code. If I create the buttons within QT5 designer, it is not possible to have them as an instance of this type of class Button in this code.
So, defining drag n drop in the QT5 designer, drag n drop of an item within a list to another list works, but i am not able to identify, which element I have dragged
If the object you are dragging from is a QWidget then you can get it through the QDropEvent source() method, and the QPushButton is a QWidget so it works in this case:
def dropEvent(self, e):
if e.source() is not None:
position = e.pos()
e.source().move(position)
e.setDropAction(Qt.MoveAction)
e.accept()
Remember to verify that it is not None since the source may be another window that is not a QWidget so you would get an exception
If you want the buttons added through Qt Designer you want them to have the same functionality then the easiest solution is to promote the buttons, there are many examples in SO where they show how to do it:
Clear QLineEdit on click event
How to insert video in ui file which made at qt designer?
Painting in a QLabel with paintEvent, etc
You are always using self.button in your drop event, so it refers to the same button. Instead you can use QDropEvent.source() to get the correct widget.
def dropEvent(self, e):
position = e.pos()
e.source().move(position) # source widget
e.setDropAction(Qt.MoveAction)
e.accept()

Increasing avaliable space in main window

I am trying to create an application using pyqt python.Application's Main window is filled with many dock widgets, some dock widgets are just used to list certain string data. These widgets are occupying more space.
the drawer to the left in the image is my interest. That drawer opens on mouse click.
Is there any way I could hide these widgets to the side of main window and open when mouse is hovered over it?
or if you know any pyqt UI element which could do this. please suggest.
The logic is to detect the desired event and show the widget, in the following example the click on the QGraphicsView is detected and then the QDockWidget that was initially hidden is shown.
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.dock_widget = QtWidgets.QDockWidget()
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock_widget)
list_widgets = QtWidgets.QListWidget()
list_widgets.addItems(["item{}".format(i) for i in range(100)])
self.dock_widget.setWidget(list_widgets)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
it = self.scene.addRect(QtCore.QRectF(0, 0, 300, 400))
it.setBrush(QtGui.QColor("white"))
self.view.viewport().installEventFilter(self)
self.setCentralWidget(self.view)
self.dock_widget.hide()
self.resize(640, 480)
for i in range(4):
self.menuBar().addAction("Action{}".format(i))
def eventFilter(self, obj, event):
if obj is self.view.viewport():
if event.type() == QtCore.QEvent.MouseButtonPress:
self.dock_widget.show()
return super().eventFilter(obj, event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

How to capture PyQt5 QMainWindow losing focus

What I want to achieve: if a user clicks outside of the QMainWindow the window should hide.
How I tried to to tackle this problem: find a way to determine if the QMainWindow lost focus, and if so, hide the window using a followup function.
Unfortunately I can not totally grasp how to achieve this.
It can be done using the flag Qt::Popup but than I am not able to give any keyboard input to the widget my QMainWindow contains.
void QApplication::focusChanged(QWidget *old, QWidget *now)
This signal is emitted when the widget that has keyboard focus changed from old to now, i.e., because the user pressed the tab-key, clicked into a widget or changed the active window. Both old and now can be the null-pointer.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyWin(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setFocus()
QtWidgets.qApp.focusChanged.connect(self.on_focusChanged)
#QtCore.pyqtSlot("QWidget*", "QWidget*")
def on_focusChanged(self, old, now):
if now == None:
print(f"\nwindow is the active window: {self.isActiveWindow()}")
# window lost focus
# do what you want
self.setWindowState(QtCore.Qt.WindowMinimized)
else: print(f"window is the active window: {self.isActiveWindow()}")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = MyWin()
MainWindow.show()
sys.exit(app.exec_())

Disable mouse wheel scroll on QscrollArea

I would like to simply disable mouse wheel scroll on QScrollArea, in order to scroll down only by clicking on right scrollbar, but I can't find any solution on the Internet.
app = QtGui.QApplication([])
sa = pg.QtGui.QScrollArea()
win = pg.GraphicsWindow()
sa.setWidget(win)
The problem is that I have a lot of graphs in my scroll area, and when I try to mousewheel on one of them, the page will scroll up or down together with the graph.
I can't find a method to call on "sa" to disable mouse wheel scroll.
I found some posts discussing about install event filter but I can't understand how to use them in this case. For example, I tried to use this:
sa.viewport().installEventFilter(???)
but I really didn't understand what arguments to pass and how to check the event.
Thank you in advance if you can help me with this problem.
You have the right idea. Event-filtering requires an object that inherits QObject to watch for the relevant events. Such objects have an eventFilter method which can be overriden to provide custom handing of all events for the watched object. If this method returns True for a given event, it will not be propagated any further. Usually the main-window is used to provide the event-filtering, like this:
import sys
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.scroll = QtGui.QScrollArea()
self.widget = QtGui.QGraphicsView()
self.widget.setFixedSize(600, 600)
self.scroll.setWidget(self.widget)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.scroll)
self.scroll.viewport().installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.Wheel and
source is self.scroll.viewport()):
return True
return super(Window, self).eventFilter(source, event)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 400, 300)
window.show()
sys.exit(app.exec_())
I finally managed to resolve this, using this solution:
class Scroller(pg.QtGui.QScrollArea):
def __init__(self):
pg.QtGui.QScrollArea.__init__(self)
def wheelEvent(self, ev):
if ev.type() == QtCore.QEvent.Wheel:
ev.ignore()
app = QtGui.QApplication([])
sa = Scroller() # <======
win = pg.GraphicsWindow()
sa.setWidget(win)

pyQt signals/slots with QtDesigner

I'm trying to write a program that will interact with QGraphicsView. I want to gather mouse and keyboard events when the happen in the QGraphicsView. For example, if the user clicks on the QGraphicsView widget I will get the mouse position, something like that. I can hard code it rather easily, but I want to use QtDesigner because the UI will be changing frequently.
This is the code that I have for the gui.py. A simple widget with a QGraphicsView in it.
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_graphicsViewWidget(object):
def setupUi(self, graphicsViewWidget):
graphicsViewWidget.setObjectName(_fromUtf8("graphicsViewWidget"))
graphicsViewWidget.resize(400, 300)
graphicsViewWidget.setMouseTracking(True)
self.graphicsView = QtGui.QGraphicsView(graphicsViewWidget)
self.graphicsView.setGeometry(QtCore.QRect(70, 40, 256, 192))
self.graphicsView.setObjectName(_fromUtf8("graphicsView"))
self.retranslateUi(graphicsViewWidget)
QtCore.QMetaObject.connectSlotsByName(graphicsViewWidget)
def retranslateUi(self, graphicsViewWidget):
graphicsViewWidget.setWindowTitle(QtGui.QApplication.translate("graphicsViewWidget", "Form", None, QtGui.QApplication.UnicodeUTF8))
The code for the program:
#!/usr/bin/python -d
import sys
from PyQt4 import QtCore, QtGui
from gui import Ui_graphicsViewWidget
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_graphicsViewWidget()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.graphicsView, QtCore.SIGNAL("moved"), self.test)
def mouseMoveEvent(self, event):
print "Mouse Pointer is currently hovering at: ", event.pos()
self.emit(QtCore.SIGNAL("moved"), event)
def test(self, event):
print('in test')
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
When I run this code, it gives me the opposite of what I want. I get the mouse position everywhere except for inside the QGraphicsView.
I'm sure it's a problem with my QObject.connect. But every time I go back and read about signals and slots it makes sense but I can't get it.
Please help, I've been banging my head for the past few days now. I'm sorry if this as been asked before but I've been through all the threads on this topic and I can't get anywhere.
Thanks
The signal must come from the QGraphicsView object that was defined in the ui.
You can create a class derived from QGraphicsView like this
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyView(QGraphicsView):
moved = pyqtSignal(QMouseEvent)
def __init__(self, parent = None):
super(MyView, self).__init__(parent)
def mouseMoveEvent(self, event):
# call the base method to be sure the events are forwarded to the scene
super(MyView, self).mouseMoveEvent(event)
print "Mouse Pointer is currently hovering at: ", event.pos()
self.moved.emit(event)
Then, in the designer:
right-click on the QGraphicsView then Promote to
write the class name in the Promoted Class Name field (e.g. "MyView"),
write the file name where that class is in the Header file field but without the .py extension,
click on the Add button and then on the Promote button.
And you can regenerate your file gui.py with pyuic4.

Categories

Resources