KeyPressEvent with Qwidget is not working? - python

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_())

Related

Move focus to another widget when the return or right arrow key is pressed?

I have a Qt widget that looks like this:
class launchiiwidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.textbox = QtWidgets.QTextEdit(self)
self.textbox.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)
self.textbox.setAlignment(QtCore.Qt.AlignCenter)
self.textbox.setFixedSize(QtCore.QSize(600, 100))
self.textbox.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.textbox.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout.addWidget(self.textbox)
font = self.textbox.font()
font.setPointSize(80)
self.textbox.setFont(font)
self.listwidget = QtWidgets.QListWidget(self)
self.listwidget.addItem("Red")
self.listwidget.addItem("Blue")
layout.addWidget(self.listwidget)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = launchiiwidget()
widget.setWindowFlags(QtCore.Qt.FramelessWindowHint)
widget.resize(600, 200)
widget.show()
sys.exit(app.exec())
How can I make it so when the "return" or "right arrow key" is pressed, focus moves from wherever it is currently to the first item in listwidget? This should also work while being focused inside of textbox, without triggering a newline.
Note: items get dynamically added to listwidget.
A possible solution could be to use QShorcut but because the OP requires "without triggering a newline". So in this case the solution is to implement an eventfilter to the QWindow:
import sys
from PyQt6 import QtCore, QtGui, QtWidgets
class KeyHelper(QtCore.QObject):
pressed = QtCore.pyqtSignal()
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 event.type() == QtCore.QEvent.Type.KeyPress:
if event.key() in (
QtCore.Qt.Key.Key_Return,
QtCore.Qt.Key.Key_Enter,
QtCore.Qt.Key.Key_Right,
):
self.pressed.emit()
return True
return super().eventFilter(obj, event)
class Launchiiwidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.textbox = QtWidgets.QTextEdit()
self.textbox.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
self.textbox.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.textbox.setFixedSize(QtCore.QSize(600, 100))
self.textbox.setVerticalScrollBarPolicy(
QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
self.textbox.setHorizontalScrollBarPolicy(
QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
font = self.textbox.font()
font.setPointSize(80)
self.textbox.setFont(font)
self.listwidget = QtWidgets.QListWidget()
self.listwidget.addItem("Red")
self.listwidget.addItem("Blue")
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.textbox)
layout.addWidget(self.listwidget)
def update_focus(self):
self.listwidget.setFocus()
index = self.listwidget.model().index(0, 0)
if index.isValid():
self.listwidget.setCurrentIndex(index)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = Launchiiwidget()
widget.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
widget.resize(600, 200)
widget.show()
key_helper = KeyHelper(widget.windowHandle())
key_helper.pressed.connect(widget.update_focus)
sys.exit(app.exec())

Moving focus to next QLineEdit using ENTER

I wrote an app to calculate amount of money by enter amount of coins and bank notes to calculate total value. I can jump to next text field using 'tab' key but I would rather to use 'enter' key. How can I set 'enter' instead of default 'tab' key for moving to next QLineEdit field ?
You have to override the event() method so that when it detects the KeyPress event and the keys are Enter then call focusNextPrevChild():
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
for i in range(10):
le = QtWidgets.QLineEdit()
lay.addWidget(le)
def event(self, event):
if event.type() == QtCore.QEvent.KeyPress:
if event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
self.focusNextPrevChild(True)
return super().event(event)
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

QComboBox click triggers a leaveEvent on the main QDialog

I am trying to build a hover Dialog but i am stuck at the interaction between QComboBox selection and QDialog's leaveEvent. it looks like when i try to click on combobox to select something, it triggers a leaveEvent which then hides my QDialog. Why is this happening? What can i try to ensure that the Dialog is only hidden when I move my mouse outside of the Dialog?
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
class hoverDialog(QDialog):
def __init__(self, parent=None):
super().__init__()
self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.v = QVBoxLayout()
self.combobox = QComboBox()
self.combobox.addItems(['Work-around','Permanent'])
self.textedit = QPlainTextEdit()
self.v.addWidget(self.combobox)
self.v.addWidget(self.textedit)
self.setLayout(self.v)
#self.setMouseTracking(True)
def leaveEvent(self, event):
self.hide()
return super().leaveEvent(event)
class Table(QWidget):
def __init__(self, parent=None):
super().__init__()
self.label4 = QLabel()
self.label4.setText("hover popup")
self.label4.installEventFilter(self)
self.checkbox1 = QCheckBox()
self.pushButton3 = QPushButton()
self.pushButton3.setText('Generate')
self.pushButton3.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
self.pushButton3.clicked.connect(self.buttonPressed)
self.hbox5 = QHBoxLayout()
self.hbox5.addWidget(self.checkbox1)
self.hbox5.addWidget(self.label4)
self.hbox5.addWidget(self.pushButton3)
self.vbox1 = QVBoxLayout()
self.vbox1.addLayout(self.hbox5)
self.setLayout(self.vbox1)
self.autoResolve = hoverDialog(self)
def eventFilter(self, obj, event):
if obj == self.label4 and event.type() == QtCore.QEvent.Enter and self.autoResolve.isHidden():
self.onHovered()
return super().eventFilter(obj, event)
def onHovered(self):
pos = QtGui.QCursor.pos()
self.autoResolve.move(pos)
self.autoResolve.show()
def buttonPressed(self):
if self.checkbox.isChecked():
print('do something..')
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Table()
form.show()
app.exec_()
Looking at the sources, I believe that the origin of the problem is in Qt's behavior whenever a new popup widget is shown.
I cannot guarantee this at 100%, but in any case the simplest solution is to hide the widget only if the combo popup is not shown:
def leaveEvent(self, event):
if not self.combobox.view().isVisible():
self.hide()
Note that your approach is not really perfect: since the dialog could be shown with a geometry that is outside the current mouse position, it will not be able to hide itself until the mouse actually enters it. You should probably filter FocusOut and WindowDeactivate events also.

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.

PyQt Dual Purpose ENTER Key Press

I’m using PyQt5 and Python 3.6. I want to use the ENTER (or RETURN) key for dual purposes.
If the user enters text into the combo box and then hits the ENTER key, then I want the text from the combo box to be appended to a list. In all other situations, I want the ENTER key to serve as a shortcut for a push button.
I can’t find the correct decision for how the handle when ENTER is pressed. Here is a code sample. I’m looking to a decision in the returnDecision(self) function (toward the bottom of the script).
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QShortcut
from PyQt5.QtWidgets import QComboBox
from PyQt5.QtGui import QKeySequence
from PyQt5.QtCore import Qt, QSize
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 200)
btn = QPushButton('Button', self)
btn.move(100, 50)
btn.clicked.connect(self.btnPrint)
self.comboBox = QComboBox(self)
self.comboBox.setEditable(True)
self.comboBox.move(100, 150)
self.comboBox.setMinimumSize(QSize(150, 0))
self.comboBox.setEditText("Initial Text")
self.comboBox.editTextChanged.connect(self.cboxPrint)
enter = QShortcut(QKeySequence(Qt.Key_Return), self)
enter.activated.connect(self.returnDecision)
self.textList = []
self.show()
def btnPrint(self):
print("Button was pressed")
def btnAction(self):
print("RETURN pressed when NOT editing combo box")
self.btnPrint()
def cboxPrint(self):
print(self.comboBox.currentText())
def cboxAction(self):
print("RETURN pressed when editing combo box")
self.textList.append(self.comboBox.currentText())
print(self.textList)
def returnDecision(self):
if ENTER KEY WAS PRESSED WHILE EDITING COMBO BOX:
self.cboxAction()
else:
self.btnAction()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Any suggestions?
One way to solve this is to use a custom subclass of the QComboBox and override the keyPressEvent method. Then also implement a keyPressEvent in your widget and handle each differently.
class CustomCombo(QtWidgets.QComboBox):
enter_pressed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.enter_pressed.emit()
else:
QtWidgets.QComboBox.keyPressEvent(self, event)
# if the key is not return, handle normally
class Example(QWidget):
def __init__(self):
# Code here
self.combo_box = CustomCombo(self)
self.combo_box.enter_pressed.connect(self.cboxAction)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.btnAction()

Categories