How to prevent a key from functioning inside QPlainTextEdit - python

Suppose I have a text edit in my GUI interface, and i press a key like a, then i should not get 'a' written on my text edit.
I can add my own function to a key press event, but i cannot prevent it from doing the default mechanism.
Help me with this please.

You can prevent the default keyPressEvent behaviour for a specific key, like this:
class CodeEditor(QtWidgets.QPlainTextEdit):
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_A:
# do nothing
event.accept()
else:
# do the default
super().keyPressEvent(event)

You can install an eventFilter and prevent the key from being transmitted.
import sys
from PyQt5.QtWidgets import QApplication, QPlainTextEdit
from PyQt5.QtCore import QObject, Qt, QEvent
class Helper(QObject):
def disable_key(self, w, key):
self.m_w = w
self.m_w.installEventFilter(self)
self.m_key = key
def eventFilter(self, obj, event):
if obj == self.m_w and event.type() == QEvent.KeyPress:
if event.key() == self.m_key:
return True
return QObject.eventFilter(self, obj, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
helper = Helper()
view = QPlainTextEdit()
helper.disable_key(view, Qt.Key_A)
view.show()
sys.exit(app.exec_())
or by letter:
import sys
from PyQt5.QtWidgets import QApplication, QPlainTextEdit
from PyQt5.QtCore import QObject, Qt, QEvent
class Helper(QObject):
def disable_key(self, w, letter):
self.m_w = w
self.m_w.installEventFilter(self)
self.m_letter = letter
def eventFilter(self, obj, event):
if obj == self.m_w and event.type() == QEvent.KeyPress:
if event.text() == self.m_letter:
return True
return QObject.eventFilter(self, obj, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
helper = Helper()
view = QPlainTextEdit()
helper.disable_key(view, "a")
view.show()
sys.exit(app.exec_())

Related

Problem in EventFilter, with Alt /Shift modifiers from QLineEdit

From QLineEdit, if I press :
Ctrl + S, it works fine
But at the same time, if I press Alt + C, or Shift+S (as per my code)
Event filter works fine, but at the same time QLineEdit Box Updated with that Pressing key.
For example if I press Alt+C from QLineEdit, the Letter "C" updated/appeared in my QLineEdit and Press Shift+S, In QLineEdit, the letter "S" is updated/appeared.
How to avoid it ?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class textbox_keypress(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("List Box Example")
self.mydesign()
# ----------------------------------------------------------------------------------
def mydesign(self):
self.textbox = QLineEdit(self)
self.textbox.setGeometry(10,10,300,30)
self.textbox.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.KeyPress and source is self.textbox:
if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_S:
print("Control + S")
if event.key() == Qt.Key_C and event.modifiers() == Qt.AltModifier:
print("Alt + C")
if event.key() == Qt.Key_E and event.modifiers() == Qt.ShiftModifier:
print("Shift + E ")
return super(textbox_keypress, self).eventFilter(source,event)
# ----------------------------------------------------------------------------------
def main():
myapp = QApplication(sys.argv)
mywindow = textbox_keypress()
mywindow.show()
sys.exit(myapp.exec_())
if __name__ =="__main__":
main()
If you want the text not to appear in the QLineEdit then you have to make the event not send to the QLineEdit, and in the case of the event filter, just return True, for example:
# ...
if event.key() == Qt.Key_C and event.modifiers() == Qt.AltModifier:
print("Alt + C")
return True
# ...

Qt Event Propagation in QWebEngineView

I have a function named generate_input_event. I'm trying to use this function to simulate a keypress within a QWebEngineView.
def generate_input_event(window_id, key_code, modifiers, low_level_data, x, y):
modifiers_flag = create_modifiers_flag(modifiers)
logging.info("generate input, window: {} code: {}, modifiers {}".format(
window_id, key_code, modifiers_flag))
event = QKeyEvent(QEvent.KeyPress, key_code, modifiers_flag)
event.artificial = True
event_window = window.get_window(window_id)
QCoreApplication.sendEvent(event_window.qtwindow, event)
Whenever I run my program and highlight an input field within my QWebEngineView, and invoke generate_input_event it is expected that it will type that letter into the input field.
I also set-up an event filter to capture everything all key presses EXCEPT for my artificially generated ones.
class EventFilter(QWidget):
def __init__(self, parent=None):
super(EventFilter, self).__init__(parent)
qApp.installEventFilter(self)
def eventFilter(self, obj, event):
if (event.type() == QEvent.KeyPress and hasattr(event, 'artificial')):
logging.info("artificial event")
return False. # send to widget
elif (event.type() == QEvent.KeyPress and not is_modifier(event.key())):
modifiers = create_modifiers_list(event.modifiers())
key_string = create_key_string(event)
key_code = event.key()
logging.info("send code: {} string: {} modifiers {}".format(
key_code, key_string, modifiers))
return True. # do not forward to widgets
return False
However when I actually run my code, this is the following output I get:
INFO:root:send code: 65 string: a modifiers ['']
INFO:root:generate input, window: 1 code: 65, modifiers <PyQt5.QtCore.Qt.KeyboardModifiers object at 0x106a4ea58>
INFO:root:artificial event
The output looks correct, HOWEVER, the input field of the QWebEngineView never actually gets a letter that was artificially generated by generate_input_event.
P.S. Should you wish to see the whole of the file/project for reasons of context, please look at this branch/file here: https://github.com/atlas-engineer/next/blob/generate_events/ports/pyqt-webengine/utility.py
Qt Webengine uses RenderWidgetHostViewQtDelegateWidget to render and this is created after loading a page so you must access it after load() or setHtml(), so that widget must send those events.
The following example will show a QWebEngineView and a QLineEdit, after showing both windows what you type in the QLineEdit will be shown in the QWebEngineView.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class EventFilter(QtCore.QObject):
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress and hasattr(
event, "artificial"
):
print("event:", event.key(), event.text())
return False
return super().eventFilter(obj, event)
class ForwardKeyEvent(QtCore.QObject):
def __init__(self, sender, receiver, parent=None):
super(ForwardKeyEvent, self).__init__(parent)
self.m_sender = sender
self.m_receiver = receiver
self.m_sender.installEventFilter(self)
def eventFilter(self, obj, event):
if self.m_sender is obj and event.type() == QtCore.QEvent.KeyPress:
# self.m_receiver.setFocus()
new_event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress,
event.key(),
event.modifiers(),
event.text(),
)
new_event.artificial = True
QtCore.QCoreApplication.postEvent(self.m_receiver, new_event)
return super().eventFilter(obj, event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ef = EventFilter()
app.installEventFilter(ef)
lineedit = QtWidgets.QLineEdit()
lineedit.show()
view = QtWebEngineWidgets.QWebEngineView()
view.resize(640, 480)
view.show()
view.load(QtCore.QUrl("https://www.google.com/"))
# RenderWidgetHostViewQtDelegateWidget is created after loading a page
# so you must access it after load() or setHtml().
render_widget = view.findChild(QtWidgets.QWidget)
print(render_widget.metaObject().className())
assert(render_widget)
fe = ForwardKeyEvent(lineedit, render_widget)
sys.exit(app.exec_())
The correct way to post and listen to events for a QWebengineview can be demonstrated with the following minimal example:
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import Qt
class ForwardKeyEvent(QtCore.QObject):
def __init__(self, sender, receiver, parent=None):
super(ForwardKeyEvent, self).__init__(parent)
self.m_sender = sender
self.m_receiver = receiver
self.m_sender.installEventFilter(self)
def eventFilter(self, obj, event):
if self.m_sender is obj and event.type() == QtCore.QEvent.KeyPress:
new_event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress,
65,
Qt.KeyboardModifiers(),
"a",
)
new_event.artificial = True
QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
return True
return False
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
lineedit = QtWidgets.QLineEdit()
lineedit.show()
view = QtWebEngineWidgets.QWebEngineView()
view.resize(640, 480)
view.show()
view.load(QtCore.QUrl("https://www.google.com/"))
# RenderWidgetHostViewQtDelegateWidget is created after loading a page
# so you must access it after load() or setHtml().
fe = ForwardKeyEvent(lineedit, view)
sys.exit(app.exec_())

installEventFilter in PyQt5

I'm trying to implement an event in PyQT5, but i get this error:
TypeError: installEventFilter(self, QObject): argument 1 has unexpected type 'MainWindow_EXEC'
This is my code
import sys
from time import sleep
from PyQt5 import QtCore, QtWidgets
from view_cortes2 import Ui_cortes2enter
class MainWindow_EXEC():
def __init__(self):
app = QtWidgets.QApplication(sys.argv)
cortes2 = QtWidgets.QMainWindow()
self.ui = Ui_cortes2()
self.ui.setupUi(cortes2)
self.flag = 0
self.ui.ledit_corteA.installEventFilter(self)
self.ui.ledit_corteB.installEventFilter(self)
self.ui.buttonGroup.buttonClicked.connect(self.handleButtons)
cortes2.show()
sys.exit(app.exec_())
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.FocusIn and source is self.ui.ledit_corteA):
print("A")
self.flag = 0
if (event.type() == QtCore.QEvent.FocusIn and source is self.ui.ledit_corteA):
print("B")
self.flag = 1
return super(cortes2, self).eventFilter(source, event)
if __name__ == "__main__":
MainWindow_EXEC()
The event that I'm trying to add is when I focus in a TextEdit it changes the value of a flag. If i change
self.ui.ledit_corteA.installEventFilter(self)
by
self.ui.ledit_corteA.installEventFilter(cortes2)
I works, but never changes the value of my flag.
Please help.
installEventFilter expects a QObject, and in your case MainWindow_EXEC is not.
If you are using the Qt Designer design it is recommended to create a new class that inherits from the appropriate widget and use the class provided by Qt Designer to fill it as shown below:
import sys
from PyQt5 import QtCore, QtWidgets
from view_cortes2 import Ui_cortes2
class MainWindow(QtWidgets.QMainWindow, Ui_cortes2):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.flag = 0
self.ledit_corteA.installEventFilter(self)
self.ledit_corteB.installEventFilter(self)
#self.buttonGroup.buttonClicked.connect(self.handleButtons)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.FocusIn and source is self.ledit_corteA:
print("A")
self.flag = 0
if event.type() == QtCore.QEvent.FocusIn and source is self.ledit_corteB:
print("B")
self.flag = 1
return super(MainWindow, self).eventFilter(source, event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
References:
http://pyqt.sourceforge.net/Docs/PyQt5/designer.html

Selection of text in QTextBrowser using mousePressEvent() and mouseReleaseEvent()

I have a QTextBrowser and I want to select a part of the text inside, I need the position of the start and the end of the selection. I want to do that with mousePressEvent and mouseReleaseEvent. Here is my code,
class MainWindow(QMainWindow, TeamInsight.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
def set_text(self):
self.textBrowser.setText('test strings are here')
textBrowser is inside a MainWindow. How do I implement mousePressEvent and mouseReleaseEvent for text in textBrowser
If you want to track events and you can not overwrite the class, the solution is to install an event filter, in your case, just the MouseButtonRelease event, we must filter the viewport() of the QTextBrowser:
import sys
from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QMainWindow, QApplication
import TeamInsight
class MainWindow(QMainWindow, TeamInsight.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.browserInput.viewport().installEventFilter(self)
self.browserInput.setText("some text")
def eventFilter(self, obj, event):
if obj is self.browserInput.viewport():
if event.type() == QEvent.MouseButtonRelease:
if self.browserInput.textCursor().hasSelection():
start = self.browserInput.textCursor().selectionStart()
end = self.browserInput.textCursor().selectionEnd()
print(start, end)
elif event.type() == QEvent.MouseButtonPress:
print("event mousePressEvent")
return QMainWindow.eventFilter(self, obj, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

How can I check if a keyboard modifier is pressed (Shift, Ctrl, or Alt)?

I am building a UI with Qt Creator and I want buttons to perform different actions with different modifiers. So I thought I could call functions with dynamic string properties that would perform the action depending on the modifier.
Is there a simpler way to do this?
It looks like all you need to do is check the keyboardModifiers in your button handler, and select a different action as appropriate. The various modifiers can be OR'd together in order to check for multi-key combinations:
PyQt5:
import sys
from PyQt5 import QtCore, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton('Test')
self.button.clicked.connect(self.handleButton)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
def handleButton(self):
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier:
print('Shift+Click')
elif modifiers == QtCore.Qt.ControlModifier:
print('Control+Click')
elif modifiers == (QtCore.Qt.ControlModifier |
QtCore.Qt.ShiftModifier):
print('Control+Shift+Click')
else:
print('Click')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
PyQt4:
import sys
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Test')
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
def handleButton(self):
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier:
print('Shift+Click')
elif modifiers == QtCore.Qt.ControlModifier:
print('Control+Click')
elif modifiers == (QtCore.Qt.ControlModifier |
QtCore.Qt.ShiftModifier):
print('Control+Shift+Click')
else:
print('Click')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I was trying to handle multiple keys pressed at the same time (e.g. A and W or W and D). The solution below works with multiple keys being pressed at the same time (including Ctrl, Shift, Alt, etc.).
def keyPressEvent(self, event):
self.firstrelease = True
astr = "pressed: " + str(event.key())
self.keylist.append(astr)
def keyReleaseEvent(self, event):
if self.firstrelease == True:
self.processmultikeys(self.keylist)
self.firstrelease = False
del self.keylist[-1]
def processmultikeys(self, keyspressed):
# Your logic here
print keyspressed
Go here for the original discussion of this solution: How to get multiple key presses in single event?
Here's another approach using bit operators, that avoids getting into many combinations.
#classmethod
def get_key_modifiers(cls):
QModifiers = Qt.QtWidgets.QApplication.keyboardModifiers()
modifiers = []
if (QModifiers & Qt.QtCore.Qt.ShiftModifier) == Qt.QtCore.Qt.ShiftModifier:
modifiers.append('shift')
if (QModifiers & Qt.QtCore.Qt.ControlModifier) == Qt.QtCore.Qt.ControlModifier:
modifiers.append('control')
if (QModifiers & Qt.QtCore.Qt.AltModifier) == Qt.QtCore.Qt.AltModifier:
modifiers.append('alt')
return modifiers

Categories

Resources