PyQt5/Python - Multiple keypress events are called with only 1 keypress - python

So I have the following basic window, my issue is that whenever I press the tab button once it triggers the event twice. Printing "tab" and "keypress" twice. I looked around and all I found about this issue was a C++ answer, I tried to understand the solution but was unable too.
from PyQt5 import QtCore, QtWidgets
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__(self)
# Install the event filter that will be used later to detect key presses
QtWidgets.qApp.installEventFilter(self)
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
def handleButton(self):
print("Button")
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress:
print("keypress")
if event.key() == QtCore.Qt.Key_Escape:
self.close()
if event.key() == QtCore.Qt.Key_Tab:
print("Tab")
pass
return super(ItemPrice, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())

The eventFilter() method needs a boolean result, or 0/1, to return if an event is relevant or not (the filter part). When you return False the event is not blocked, and will hit his target, this lets the application handle the event in a normal way.
In your example, when an expected key is pressed (this is a relevant event), you need to return 1 or True to "intercept" it and prevent the program to handle it, since you provide your own process. In the other cases, you can call the super method like you did :
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress:
print("keypress")
if event.key() == QtCore.Qt.Key_Escape:
self.close()
return 1
if event.key() == QtCore.Qt.Key_Tab:
print("Tab")
return 1
return super().eventFilter(obj, event)

Related

KeyPressEvent with Qwidget is not working?

If I press any key, nothing will happen. How to implement it? And I notice if I pressed some key, (at first time/Very Beggning/first character), it take some more time to display that pressed Character in textbox.How to resolve it?
from PyQt5 import QtWidgets,QtCore,QtGui
class MyKey(QtWidgets.QWidget):
def __init__(self):
super(). __init__()
self.setWindowTitle(" My Key Board")
self.ui()
def ui(self):
self.tb = QtWidgets.QLineEdit()
self.vb = QtWidgets.QVBoxLayout()
self.vb.addWidget(self.tb)
self.setLayout(self.vb)
def keyPressEvent(self, e):
print("key pressed")
print(e.key())
if type(e) == QtGui.QKeyEvent:
if e.key() == QtCore.Qt.Key_A:
print("ypu pressed 'A'")
if __name__ == "__main__":
import sys
myapp = QtWidgets.QApplication(sys.argv)
mywindow = MyKey()
mywindow.show()
sys.exit(myapp.exec_())
In Qt the KeyPressEvent is only sent to the widget that has the focus and if it consumes it then the event is not sent to the parent widgets otherwise the parent widget is also notified.
In this case, the QLineEdit is the widget that has the focus and consumes the events that have text, numbers and some special keys, so the parent widgets will not be notified in those cases. There are other keys that are not consumed by QLineEdit such as F2, Escape, etc that are received by the parent widget.
If you want to listen to a special key or combination of keys then you must use a QShortcut but because the OP wants to listen to all the events then a possible solution is to install an eventfilter to the QWindow associated with the window that if it receives the keyboard event when the window has the focus no matter which widget has the focus.
from PyQt5 import QtWidgets, QtCore, QtGui
class MyKey(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle(" My Key Board")
self.ui()
def ui(self):
self.tb = QtWidgets.QLineEdit()
vb = QtWidgets.QVBoxLayout(self)
vb.addWidget(self.tb)
def handle_key_press(self, key, text):
if key == QtCore.Qt.Key_A:
print("ypu pressed 'A'")
class KeyHelper(QtCore.QObject):
key_pressed = QtCore.pyqtSignal(int, str)
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 obj is self._window and event.type() == QtCore.QEvent.KeyPress:
self.key_pressed.emit(event.key(), event.text())
return super().eventFilter(obj, event)
if __name__ == "__main__":
import sys
myapp = QtWidgets.QApplication(sys.argv)
mywindow = MyKey()
mywindow.show()
helper = KeyHelper(mywindow.windowHandle())
helper.key_pressed.connect(mywindow.handle_key_press)
sys.exit(myapp.exec_())

How to deactivate My PyQt5 checkbox, when textbox is empty?

My aim to deactivate the checkbox, if my textbox is empty. So I try with the following code if I clear my entire textbox by the way of "select all characters" using Ctrl+A and delete it. But in my code, the first part only worked, that is Ctrl+A, If I press the Delete key that is the second part of my code is not worked, How to rectify?
if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_A :
print("I am inside of Cntrl+A")
if event.key() == Qt.Key_Delete:
print("i Am inside of key_delete")
self.checkbox.setChecked(False)
self.checkstatus = 0
return True
Minimal reproducible Code
import os
from PyQt5.QtWidgets import QCheckBox,QLineEdit,QWidget,QApplication,QVBoxLayout
from PyQt5.QtCore import QEvent,Qt
class CtrlaDel(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Key Press Event")
self.le = QLineEdit()
self.cb = QCheckBox()
self.cb.setChecked(True)
self.vbox = QVBoxLayout(self)
self.vbox.addWidget(self.le)
self.vbox.addWidget(self.cb)
self.le.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.KeyPress and source is self.le:
print("ddddsdsdsdsdsdsds")
if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete and source is self.le:
print("ddd")
if len(self.le.text()) <= 1:
self.cb.setChecked(False)
if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_A:
print("I am inside of Ctrl+A")
if event.key() == Qt.Key_Delete:
print("I am Inside of Delete")
self.cb.setChecked(False)
self.checkstatus = 0
return True
return super(CtrlaDel, self).eventFilter(source, event)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainwindow = CtrlaDel()
mainwindow.show()
sys.exit(app.exec_())
I do not see the need to use an eventFilter, the solution is that every time the text is changed verify that if it is an empty text then set the checkbox to false and for this the textChanged signal can be used
from PyQt5.QtWidgets import QCheckBox, QLineEdit, QWidget, QApplication, QVBoxLayout
class CtrlaDel(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Key Press Event")
self.le = QLineEdit()
self.cb = QCheckBox()
self.cb.setChecked(True)
vbox = QVBoxLayout(self)
vbox.addWidget(self.le)
vbox.addWidget(self.cb)
self.le.textChanged.connect(self.handle_text_changed)
self.handle_text_changed()
def handle_text_changed(self):
if not self.le.text():
self.cb.setChecked(False)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainwindow = CtrlaDel()
mainwindow.show()
sys.exit(app.exec_())

How to detect a mouseButtonPressed event outside a QGraphicsView widget?

I have a QGraphicsView widget with a QGraphicsScene set in it. I wish to detect a mouseButtonPressed event anywhere in my application window outside of the QGraphicsView widget.
I tried to install an eventFilter to the central widget of the application as follows:
self.centralwidget.installEventFilter(self)
My QGraphicsView widget is self.viewStartImg. In my eventFilter method, I have the following:
def eventFilter(self, obj, event):
if obj != self.viewStartImg and event.type() == QEvent.MouseButtonPress:
print('Outside the QGraphicsView')
In my application, when I click inside of the QGraphicsView, I still get 'Outside the QGraphicsView' printed out. I believe that's happening because QGraphicsView is a child of the centralWidget, but I am not sure.
Any alternate method of achieving this functionality is highly appreciated!
There are several ways to achieve this. One way would be to subclass QGraphicsView and override mousePressEvent in the subclass. Another possibility is to install an event filter on the view object. For the first method you would do something like this
from PyQt5 import QtWidgets, QtCore
class MyView(QtWidgets.QGraphicsView):
def mousePressEvent(self, event):
print('mouse pressed inside view')
event.accept()
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.central = QtWidgets.QWidget(self)
self.view = MyView(self.central)
self.scene = QtWidgets.QGraphicsScene()
self.view.setScene(self.scene)
hlayout = QtWidgets.QHBoxLayout(self.central)
hlayout.setContentsMargins(50,50,50,50)
hlayout.addWidget(self.view)
self.setCentralWidget(self.central)
def mousePressEvent(self, event):
print('mouse pressed ouside view')
event.accept()
if __name__ == "__main__" :
app = QtWidgets.QApplication([])
win = MyWindow()
win.show()
app.exec()
And for the second one
class MyWindow(QtWidgets.QMainWindow):
money_changed = QtCore.pyqtSignal(int)
def __init__(self):
super().__init__()
self.central = QtWidgets.QWidget(self)
self.view = QtWidgets.QGraphicsView(self.central)
self.scene = QtWidgets.QGraphicsScene()
self.view.setScene(self.scene)
hlayout = QtWidgets.QHBoxLayout(self.central)
hlayout.setContentsMargins(50,50,50,50)
hlayout.addWidget(self.view)
self.setCentralWidget(self.central)
self.view.installEventFilter(self)
def mousePressEvent(self, event):
print('mouse pressed ouside view')
event.accept()
def eventFilter(self, object, event):
if object == self.view and event.type() == QtCore.QEvent.MouseButtonPress:
print('mouse pressed inside view')
return True
return super().eventFilter(object, event)
It may not be exactly what you're looking for, but I recently needed to register when the user clicked outside my widget and used this method:
def event(self, event):
if event == None:
return False
if event.type() == QtCore.QEvent.WindowDeactivate:
print "Clicked outside"
self.main_widget.close()
return super(_ComboBoxPlusDialog, self).event(event)
I used this to close the window, as you can see. So it only registers the outside click once. I used this to get around an issue with Qt.Popup disabling input for the IME.

Why mouseMoveEvent does nothing in PyQt5

I try to use mouseMoveEvent and mousePressEvent in PyQt5 and Python3.5, but there is nothing when I click my mouse. My code is as following, is there something wrong?
from PyQt5 import QtWidgets, QtGui, QtCore
class Window(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
widget = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(widget)
self.graphicsView = QtWidgets.QGraphicsView()
self.graphicsView.setCursor(QtCore.Qt.CrossCursor)
self.graphicsView.setObjectName("graphicsView")
layout.addWidget(self.graphicsView)
self.setCentralWidget(widget)
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.NoButton:
print("Simple mouse motion")
elif event.buttons() == QtCore.Qt.LeftButton:
print("Left click drag")
elif event.buttons() == QtCore.Qt.RightButton:
print("Right click drag")
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
print("Press!")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
Firstly, you must enable mouse-tracking:
self.graphicsView.setMouseTracking(True)
Then you can either use a subclass of QGraphicsView:
class GraphicsView(QtWidgets.QGraphicsView):
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.NoButton:
print("Simple mouse motion")
elif event.buttons() == QtCore.Qt.LeftButton:
print("Left click drag")
elif event.buttons() == QtCore.Qt.RightButton:
print("Right click drag")
super(GraphicsView, self).mouseMoveEvent(event)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
print("Press!")
super(GraphicsView, self).mousePressEvent(event)
Or install an event-filter:
self.graphicsView.viewport().installEventFilter(self)
...
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.MouseMove:
if event.buttons() == QtCore.Qt.NoButton:
print("Simple mouse motion")
elif event.buttons() == QtCore.Qt.LeftButton:
print("Left click drag")
elif event.buttons() == QtCore.Qt.RightButton:
print("Right click drag")
elif event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.LeftButton:
print("Press!")
return super(Window, self).eventFilter(source, event)
I am sure that your events is handled inside QGraphicsView. You have to read more about event propagation. Try it without adding any additional widgets on top of Window. And do not forget abt MouseTracking property, which is false by default and mouse move events without buttons do not happend at all.
I would recomend to read this article. It's is quite old, but still relevant. Also mouse events in QGraphicsView handled in a different way, read docs for more details.
Sory no code samples since I am C++ developer.

How to change minimize event behavior in PyQt or PySide?

I'm developing a Qt application and changed the closing behavior with the closeEvent virtual function this way:
class MainWindow(QMainWindow):
def closeEvent(self, event):
event.ignore()
self.hide()
self.trayicon.showMessage('Running', 'Running in the background.')
This works as expected. If I remove event.ignore() the application quits as expected, everything is fine.
I want to control the minimize event too, so when the user clicks the minimize button on the title bar, I want to move the window instead of minimize.
I cannot use the hideEvent virtual function, because the event will be sent to the window anyway, so this code:
def hideEvent(self, event):
event.ignore()
self.move(0,0)
moves the window to the top left AND then minimize it. event.ignore() has no effect here, so I tried using QtCore.QObject.event this way:
def event(self, event):
if event.type() == QEvent.WindowStateChange:
if self.isMinimized():
event.ignore()
self.move(0,0)
return True
return False
The window moves but minimizes again. What is wrong with this ? How can I override the minimize event completely ?
Try the changeEvent and filter for WindowMinimized events, something like this:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.systemTrayIcon = QtGui.QSystemTrayIcon(self)
self.systemTrayIcon.setIcon(QtGui.QIcon.fromTheme("face-smile"))
self.systemTrayIcon.setVisible(True)
self.systemTrayIcon.activated.connect(self.on_systemTrayIcon_activated)
self.label = QtGui.QLabel(self)
self.label.setText("Minimize me!")
self.layoutVertical = QtGui.QVBoxLayout(self)
self.layoutVertical.addWidget(self.label)
#QtCore.pyqtSlot(QtGui.QSystemTrayIcon.ActivationReason)
def on_systemTrayIcon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.DoubleClick:
if self.isHidden():
self.show()
else:
self.hide()
def changeEvent(self, event):
if event.type() == QtCore.QEvent.WindowStateChange:
if self.windowState() & QtCore.Qt.WindowMinimized:
event.ignore()
self.close()
return
super(MyWindow, self).changeEvent(event)
def closeEvent(self, event):
event.ignore()
self.hide()
self.systemTrayIcon.showMessage('Running', 'Running in the background.')
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())

Categories