I try to add mouse events to a new QAction object. I want to use them in custom menu's.
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QEvent, pyqtSignal as Signal, pyqtSlot as Slot
class MouseEvent(QEvent):
def __init__(self):
super(MouseEvent,self).__init__(QEvent.Type(QEvent.MouseButtonRelease))
class MyAction(QtGui.QAction):
clicked = Signal()
def __init__(self, name, parent):
super(MyAction, self).__init__(name, parent)
self.customEvent(MouseEvent)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
event.accept()
self.clicked.emit()
self.rightClicked(event)
else:
event.ignore()
#Slot()
def rightClicked(self, event):
print "right clicked"
return event
class AnyApplication(QtGui.QMainWindow):
def __init__(self):
super(AnyApplication, self).__init__()
self.UI()
def UI(self):
menuBar = self.menuBar()
m = menuBar.addMenu("Edit")
a = MyAction("Do", m)
#a.clicked.connect(self.doMoreWithClicked)
m.addAction(a)
def doMorWithClicked(self):
print "Do more.."
app = QtGui.QApplication(sys.argv)
anyApp = AnyApplication()
anyApp.show()
sys.exit(app.exec_())
I get the following error:
Traceback (most recent call last):
...
self.event(MouseEvent)
TypeError: QAction.event(QEvent): argument 1 has unexpected type 'PyQt4.QtCore.pyqtWrapperType'
I really want to understand how I create my own events and combine them with signals and slots.
Thanks...
Related
I have a QWidget containing another (child) widget for which I'd like to process hoverEnterEvent and hoverLeaveEvent. The documentation mentions that
Mouse events occur when a mouse cursor is moved into, out of, or within a widget, and if the widget has the Qt::WA_Hover attribute.
So I tried to receive the hover events by setting this attribute and implementing the corresponding event handlers:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
class TestWidget(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
layout.addWidget(TestLabel('Test 1'))
layout.addWidget(TestLabel('Test 2'))
self.setLayout(layout)
self.setAttribute(Qt.WA_Hover)
class TestLabel(QLabel):
def __init__(self, text):
super().__init__(text)
self.setAttribute(Qt.WA_Hover)
def hoverEnterEvent(self, event): # this is never invoked
print(f'{self.text()} hover enter')
def hoverLeaveEvent(self, event): # this is never invoked
print(f'{self.text()} hover leave')
def mousePressEvent(self, event):
print(f'{self.text()} mouse press')
app = QApplication([])
window = TestWidget()
window.show()
sys.exit(app.exec_())
However it doesn't seem to work, no hover events are received. The mousePressEvent on the other hand does work.
In addition I tried also the following things:
Set self.setMouseTracking(True) for all widgets,
Wrap the TestWidget in a QMainWindow (though that's not what I want to do for the real application),
Implement event handlers on parent widgets and event.accept() (though as I understand it, events propagate from inside out, so this shouldn't be required).
How can I receive hover events on my custom QWidgets?
The QWidget like the QLabel do not have the hoverEnterEvent and hoverLeaveEvent methods, those methods are from the QGraphicsItem so your code doesn't work.
If you want to listen to the hover events of the type you must override the event() method:
import sys
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
class TestWidget(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
layout.addWidget(TestLabel("Test 1"))
layout.addWidget(TestLabel("Test 2"))
class TestLabel(QLabel):
def __init__(self, text):
super().__init__(text)
self.setAttribute(Qt.WA_Hover)
def event(self, event):
if event.type() == QEvent.HoverEnter:
print("enter")
elif event.type() == QEvent.HoverLeave:
print("leave")
return super().event(event)
def main():
app = QApplication(sys.argv)
window = TestWidget()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Did you know that you can do this with QWidget's enterEvent and leaveEvent? All you need to do is change the method names. You won't even need to set the Hover attribute on the label.
from PyQt5.QtWidgets import QApplication, QGridLayout, QLabel, QWidget
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
layout = QGridLayout()
self.label = MyLabel(self)
layout.addWidget(self.label)
self.setLayout(layout)
text = "hover label"
self.label.setText(text)
class MyLabel(QLabel):
def __init__(self, parent=None):
super(MyLabel, self).__init__(parent)
self.setParent(parent)
def enterEvent(self, event):
self.prev_text = self.text()
self.setText('hovering')
def leaveEvent(self, event):
self.setText(self.prev_text)
if __name__ == "__main__":
app = QApplication([])
w = Window()
w.show()
app.exit(app.exec_())
Assuming that I have a QPushButton named button, I successfully do the following to allow click event:
class UI(object):
def setupUi(self, Dialog):
# ...
self.button.clicked.connect(self.do_something)
def do_something(self):
# Something here
My question is: how can I call do_something() when the tab key is pressed? For instance, if I have a QlineEdit named tb_id, after entering a value and press tab key, do_something() method should be called in the same way as what clicked does above. How can I do that using pyqt5?
Thank you so much.
To get what you want there are many methods but before pointing it by observing your code I see that you have generated it with Qt Designer so that code should not be modified but create another class that uses that code so I will place the base code:
from PyQt5 import QtCore, QtWidgets
class UI(object):
def setupUi(self, Dialog):
self.button = QtWidgets.QPushButton("Press Me")
lay = QtWidgets.QVBoxLayout(Dialog)
lay.addWidget(self.button)
class Dialog(QtWidgets.QDialog, UI):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setupUi(self)
self.button.clicked.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print("do_something")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Also, I recommend you read the difference between event and signal in the world of Qt in What are the differences between event and signal in Qt since you speak of click event but in the world of Qt one must say clicked signal.
Now if going to the point there are the following options:
Using keyPressEvent:
class Dialog(QtWidgets.QDialog, UI):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setupUi(self)
self.button.clicked.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print("do_something")
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Tab:
self.do_something()
Using an event filter:
class Dialog(QtWidgets.QDialog, UI):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setupUi(self)
self.button.clicked.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print("do_something")
def eventFilter(self, obj, event):
if obj is self and event.type() == QtCore.QEvent.KeyPress:
if event.key() == QtCore.Qt.Key_Tab:
self.do_something()
return super(Dialog, self).eventFilter(obj, event)
Using activated signal of QShorcut:
class Dialog(QtWidgets.QDialog, UI):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setupUi(self)
self.button.clicked.connect(self.do_something)
shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Tab), self)
shortcut.activated.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print("do_something")
From the previous methods I prefer the latter because you do not need to overwrite anything and you can connect to several functions.
On the other hand, only events are detected when the focus is in the Qt window.
I assume you put your widget in QDialog widget, so if you want to implement your own key press event, you should override the keyPressEvent of your Dialog widget,
then it can behave as you like.
Here's an example,
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog
class UI(QDialog):
def __init__(self):
super(UI, self).__init__()
# ...
self.button.clicked.connect(self.do_something)
def do_something(self):
# Something here
def keyPressEvent(self, event):
# when press key is Tab call your function
if event.key() == Qt.Key_Tab:
self.do_something()
I'm trying to figure out how dnd works with (py)qt. I tried several example applications and.. hm was wondering: how do I see what data is actually inside a drag/drop event? I tried printing the event, the dict of the event, the mimeData().text().. but no mater what i drag in there, there seems to be no data whatsoever. Eg if I drag over a '.desktop' file from /usr/share/applications, shouldn't there be info inside the drag event?
see this eg. application:
#!/usr/bin/python
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import Qt
class DragFromWidget(QtGui.QDockWidget):
def __init__(self, parent=None):
super(DragFromWidget, self).__init__(parent=parent)
self.layout().addWidget(QtGui.QLabel("Label!"))
class DragToWidget(QtGui.QDockWidget):
def __init__(self, parent=None):
super(DragToWidget, self).__init__(parent=parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
#HOW?
print event
print event.__dict__
print event.mimeData()
print event.mimeData().__dict__
print event.mimeData().text()
event.accept()
def dropEvent(self, event):
#HOW?
print event
print event.__dict__
print event.mimeData()
print event.mimeData().__dict__
print event.mimeData().text()
class SandboxApp(QtGui.QApplication):
def __init__(self, *args, **kwargs):
super(SandboxApp, self).__init__(*args)
self.mainwindow = MainWindow()
self.mainwindow.show()
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks|QtGui.QMainWindow.AnimatedDocks)
self.addDockWidget(Qt.LeftDockWidgetArea, DragFromWidget())
self.addDockWidget(Qt.RightDockWidgetArea, DragToWidget())
def main():
app = SandboxApp(sys.argv)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have the code below for a interactive label in PyQt4 that can be clicked, right clicked and scrolled. I am converting the code for PyQt5 and lots of things in my code are right now based on this element.
class ExtendedQLabel(QLabel):
def __init(self, parent):
super().__init__(parent)
def mousePressEvent(self, ev):
if ev.button() == QtCore.Qt.RightButton:
self.emit(SIGNAL('rightClicked()'))
else:
self.emit(SIGNAL('clicked()'))
def wheelEvent(self, ev):
self.emit(SIGNAL('scroll(int)'), ev.delta())
How do I make this PyQt5 compatible?
Ok, after a lot of things I understood what I was doing wrong:
from PyQt5 import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class ExtendedQLabel(QLabel):
def __init(self, parent):
super().__init__(parent)
clicked = pyqtSignal()
rightClicked = pyqtSignal()
def mousePressEvent(self, ev):
if ev.button() == Qt.RightButton:
self.rightClicked.emit()
else:
self.clicked.emit()
if __name__ == '__main__':
app = QApplication([])
eql = ExtendedQLabel()
eql.clicked.connect(lambda: print('clicked'))
eql.rightClicked.connect(lambda: print('rightClicked'))
eql.show()
app.exec_()
In the line with the text clicked = pyqtSignal() and rightClicked = pyqtSignal()what ties those signals to that class that makes the code above work? Well the answer is the correct indentation, correct indentation will nest the variable to the class instead of randomly creating a variable that has no use. It took me a lot of time to perceive this, so thought lefting this here could be useful if someone google this.
from PyQt5 import QtGui,QtCore,QtWidgets
class Clickable_Label(QtWidgets.QLabel):
def __init__(self):
super().__init__()
clicked = QtCore.pyqtSignal() # signal when the text entry is left clicked
def mousePressEvent(self, event):
self.clicked.emit()
QtWidgets.QLabel.mousePressEvent(self, event)
myLabel=Clickable_Label()
myLabel.setText("Clickable_Label")
myLabel.board_rule_download.clicked.connect(lambda: print('clicked'))`enter code here`
I need to enable a button in my app whenever something is dropped to my custom QTreeWidget.
I sub-classed QTreeWidget to implement drag and drop of custom data. But I'm not able to find a way to get notified when something is dropped into my custom QTreeWidget. I couldn't find a QTreeWidget signal to do this. Of course, the QTreeWidget's dropEvent() will be called each time something is dropped but that doesn't help much to achieve what I'm trying to do.
Here's where I instantiate the custom QTreeWidget that accepts drops from another widget,
from PyQt4 import QtCore, QtGui
import MyTreeWidget
class TestWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(TestWindow, self).__init__(parent)
self.myTreeWidget = MyTreeWidget.MyTreeWidget()
...
#self.myTreeWidget.onItemDropped.connect(self.doSomethingOnItemDropped) <== I am looking for something like this
def doSomethingOnItemDropped(self):
# Enable certain button
...
And then, here is how I sub-classed QTreeWidget,
import sys
from PyQt4 import QtGui, QtCore
class MyTreeWidget(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(MyTreeWidget, self).__init__(parent)
self.setAcceptDrops(True)
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
event.acceptProposedAction()
data = event.mimeData().data("application/x-icon-and-text")
stream= QtCore.QDataStream(data, QtCore.QIODevice.ReadOnly)
text = QtCore.QString()
icon = QtGui.QIcon()
stream >> text >> icon
item = QtGui.QTreeWidgetItem(self)
item.setText(0, text)
item.setIcon(0, icon)
self.addTopLevelItem(item)
else:
event.ignore()
def dragEnterEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("application/x-icon-and-text"):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
event.ignore()
Any ideas? Thanks!
UPDATE : This is what worked for me
Based on comment by #ekhumoro I defined a custom signal itemDropped for my custom QTreeWidget which emits in dropEvent() event handler.
import sys
from PyQt4 import QtGui, QtCore
class MyTreeWidget(QtGui.QTreeWidget):
itemDropped = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(MyTreeWidget, self).__init__(parent)
self.setAcceptDrops(True)
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
event.acceptProposedAction()
data = event.mimeData().data("application/x-icon-and-text")
stream= QtCore.QDataStream(data, QtCore.QIODevice.ReadOnly)
text = QtCore.QString()
icon = QtGui.QIcon()
stream >> text >> icon
item = QtGui.QTreeWidgetItem(self)
item.setText(0, text)
item.setIcon(0, icon)
self.addTopLevelItem(item)
self.itemDropped.emit()
else:
event.ignore()
And in my app,
from PyQt4 import QtCore, QtGui
import MyTreeWidget
class TestWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(TestWindow, self).__init__(parent)
self.myTreeWidget = MyTreeWidget.MyTreeWidget()
self.myTreeWidget.itemDropped.connect(self.doSomethingOnItemDropped)
...
def doSomethingOnItemDropped(self):
# Enable certain button
...
You could define a custom signal and emit it from dropEvent:
class MyTreeWidget(QtGui.QTreeWidget):
itemDropped = QtCore.pyqtSignal()
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
...
self.itemDropped.emit()
In PySide2 you can use this:
class MyTreeWidget(QtGui.QTreeWidget):
itemDropped = QtCore.Signal()
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
self.itemDropped.emit()