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()
Related
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_())
Hi everyone so I was doing some exam prep with PYQT5 and I created an application from an exercise in my workbook where we have to make it display a list of courses and when you click them it opens a message box with the course name also there is a button so that the user can add a course to the list. The add button is suppoused to open a QlineEdit on the last item in the listWidget, so the user can edit the field however I keep getting a TypeError message:
line 67, in onAddButton
self.mylistWidget.openPersistentEditor(self, modelItem)
TypeError: openPersistentEditor(self, QListWidgetItem): argument 1 has unexpected type 'UNISACourses'
import sys
from PyQt5.QtWidgets import (QListWidget, QLineEdit, QWidget, QMessageBox, QHBoxLayout, QAbstractItemView,
QApplication, QVBoxLayout, QPushButton, QButtonGroup)
from PyQt5.QtCore import pyqtSlot
from PyQt5 import Qt, QtGui
class MyListWidget(QListWidget, QLineEdit, QWidget):
"""
Subclassed QListWidget to allow for the closing of open editors and other modifications
"""
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return:
print("Closing any persistent editor")
self.closePersistentEditor(self.model().index(self.count() - 1))
else:
super().keyPressEvent(event)
class UNISACourses(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# Main Window Settings
self.setGeometry(300, 300, 350, 250)
self.setWindowTitle('Courses')
# Layout
self.main_layout = QVBoxLayout(self)
self.setLayout(self.main_layout)
# Main Widget
self.mylistWidget = MyListWidget()
self.mylistWidget.addItems(["COS1511", "COS1521", "COS1512", "MNB1512", "INF1505", "FAC1502"])
self.main_layout.addWidget(self.mylistWidget)
# Define a layout for the other buttons to exist in for flexibility with resizing
self.btn_add = QPushButton("Add", clicked=self.onAddButton)
self.btn_delete = QPushButton("Delete", clicked=self.onDeleteButton)
hbox = QHBoxLayout()
hbox.addWidget(self.btn_add)
hbox.addWidget(self.btn_delete)
self.main_layout.addLayout(hbox)
# Define any additional behavior of the list
self.mylistWidget.itemDoubleClicked.connect(self.onClicked)
def onClicked(self, item):
QMessageBox.information(self, "Info", item.text())
#pyqtSlot()
def onAddButton(self):
"""
Opens a QLineEdit editor on the last item in the listwidget, allowing the user to edit the field.
NOTE: The user must click outside of the editor field then press Enter in order to close the editor
"""
self.mylistWidget.addItem('')
modelItem = self.mylistWidget.model().index(self.mylistWidget.count() - 1)
self.mylistWidget.openPersistentEditor(self, modelItem)
#pyqtSlot()
def onDeleteButton(self):
for item in self.mylistWidget.selectedItems():
self.mylistWidget.takeItem(self.mylistWidget.row(item))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = UNISACourses()
ex.show()
sys.exit(app.exec_())
You are passing two incorrect arguments (self and a QModelIndex) to QListWidget.openPersistentEditor which accepts one QListWidgetItem. Use the QListWidget.item method to get the item. You can also add QListWidget.setCurrentItem so it gets selected right away and ready to edit.
def onAddButton(self):
self.mylistWidget.addItem('')
modelItem = self.mylistWidget.item(self.mylistWidget.count() - 1)
self.mylistWidget.openPersistentEditor(modelItem)
self.mylistWidget.setCurrentItem(modelItem)
Same fix here:
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return:
print("Closing any persistent editor")
self.closePersistentEditor(self.item(self.count() - 1))
else:
super().keyPressEvent(event)
Also the Qt Namespace class for Qt.Key_Return is inside the QtCore Module.
from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5 import QtGui
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.
I want show text after enter key pressed
from PyQt5.QtWidgets import QWidget, QApplication, QPlainTextEdit, QVBoxLayout, QLabel
from PyQt5.QtCore import Qt
import sys
class PlainTextEdit(QPlainTextEdit):
def __init__(self, parent):
super().__init__(parent=parent)
close_window = ClosingWindow()
vbox = QVBoxLayout()
self.close_window.setLayout(vbox)
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Enter:
self.close_window.label_display.setText("Enter key pressed")
class ClosingWindow(QWidget):
def __init__(self):
super().__init__()
plainText = PlainTextEdit(self)
self.initUI()
def initUI(self):
vbox = QVBoxLayout()
label_display = QLabel("Text Here")
self.setLayout(vbox)
self.setWindowTitle("Message Box")
self.setGeometry(200, 200, 500, 300)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
close_win = ClosingWindow()
sys.exit(app.exec_())
Your code has the following errors:
You are creating an infinite loop: You are creating a PlainTextEdit within a ClosingWindow, and in that PlainTextEdit you are creating another ClosingWindow, and in that other ClosingWindow you are creating a PlainTextEdit, etc. Every time you use the constructor of a class you are creating a different object, so the "close_window" created in PlainTextEdit is different from the "close_win".
Each class must have a single responsibility (1), in your case the responsibility of PlainTextEdit is to notify that it was pressed enter, and in that case you must use a signal.
The enter key on the keyboard does not correspond to Qt::Key_Enter but Qt::Key_Return, it only corresponds to Qt::Key_Return on the keypad.
It is not necessary to create a layout in PlainTextEdit.
Considering the above, the solution is:
from PyQt5 import QtCore, QtWidgets
class PlainTextEdit(QtWidgets.QPlainTextEdit):
sendTextSignal = QtCore.pyqtSignal(str)
def keyPressEvent(self, event):
if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
self.sendTextSignal.emit("Enter key pressed")
else:
self.sendTextSignal.emit("Not Enter key pressed")
super().keyPressEvent(event)
class ClosingWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
plainText = PlainTextEdit()
label_display = QtWidgets.QLabel("Text Here")
plainText.sendTextSignal.connect(label_display.setText)
vbox = QtWidgets.QVBoxLayout(self)
vbox.addWidget(plainText)
vbox.addWidget(label_display)
self.setWindowTitle("Message Box")
self.setGeometry(200, 200, 500, 300)
self.show()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
close_win = ClosingWindow()
sys.exit(app.exec_())
(1) Single responsibility principle
This question already has an answer here:
How to capture the Key_tab event
(1 answer)
Closed 4 years ago.
With the code below, every key prints something in the terminal except for the tab key. The tab key still works though, I'm able to tab between line edits. I just can capture the event.
# i'm using PyQt5==5.11.3 and 32 bit python 3.7.1
from PyQt5.QtWidgets import QLineEdit, QLabel, QWidget, QVBoxLayout, QApplication
import sys
class Main(QWidget):
def __init__(self):
super().__init__()
label = QLabel('event')
input1 = Input()
input2 = Input()
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(input1)
layout.addWidget(input2)
self.setLayout(layout)
self.show()
class Input(QLineEdit):
def __init__(self):
super().__init__()
def keyPressEvent(self, event):
# why doesn't tab print anything
print(event.key())
if __name__ == "__main__":
app = QApplication(sys.argv)
wid = Main()
sys.exit(app.exec_())
You can intercept tke Tab pressed event using the event method from QLineEdit. You process your event, then you pass it to the QLineEdit.event() method.
Something like that:
import sys
from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtWidgets import QLineEdit, QLabel, QWidget, QVBoxLayout, QApplication
class Main(QWidget):
def __init__(self):
super().__init__()
label = QLabel('event')
input1 = Input()
input2 = Input()
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(input1)
layout.addWidget(input2)
self.setLayout(layout)
self.show()
class Input(QLineEdit):
def __init__(self):
super().__init__()
def keyPressEvent(self, event):
print(event.key())
def event(self,event):
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
self.tabFollow()
return QLineEdit.event(self,event)
def tabFollow(self):
print("tab-key pressed!")
if __name__ == "__main__":
app = QApplication(sys.argv)
wid = Main()
sys.exit(app.exec_())