QTextEdit - How to "cancel" entered key codes in onKeyPress() - c++

I am trying to "cancel" key codes in QTextEdit or QPlainTextEdit. When I say cancel, what I mean is, I want to turn the "entered" character into "nothing" depending on the key entered. Example: if the user hits "a" or "b" on the keyboard, I would not want to have "a" or "b" displayed / entered into the text, instead, the input would be ignored and turned into nothing / won't be processed.
With C++ Builder, you have a KeyDown_Event and a "Key" parameter. Once you detect the entered key code, if you don't like it, you can set the "Key" parameter to 0, so you set "Key = 0" and the key stroke would not be displayed. How do I achieve the same thing in Qt?
Let me explain with code:
if (e->key() == 67)
// do not send "c" to the QTextEdit (In C++ Bullder, you would do Key = 0)
if (e->key() == 65)
// do not send "a" to the QTextEdit (In C++ Bullder, you would do Key = 0)
How do I do this in Qt?
I tired doing e->setAccepted(false) and e->Ignore() but it made no difference. I think by the time e->ignore() is executed, the "char" is already inserted into the text box. With C++ Builder, you can intercept this with the KeyDown event and cancel it. I can't seem to find a way with Qt.
Thx

Similar to void QObject::installEventFilter ( QObject * filterObj ) example:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
textEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched == textEdit && event->type() == QEvent::KeyPress) {
QKeyEvent *e = static_cast < QKeyEvent * >(event);
if (e->key() == Qt::Key_A) {
return true;
}
}
return QMainWindow::eventFilter(watched, event);
}
UPDATE
As IInspectable noticed, this won't help you with filtering Ctrl+C/Ctrl+V method. If you need these either, you'll need to connect to QTextEdit::textChanged signal and updated the text manually. Something like this:
static QString oldString;
QString s = textEdit->toPlainText();
if (s == oldString)
return;
int pos = textEdit->textCursor().position();
s.remove('a', Qt::CaseInsensitive);
oldString = s;
textEdit.setPlainText(s);
QTextCursor cursor = textEdit->textCursor();
cursor.setPosition(pos);
textEdit->setTextCursor(cursor);

Related

Mouse right click option using eventFilter in Qt

I have QGraphicsView, which has many QGraphicsItem. I am trying to create a right click menu on these QGraphicsItem. Right click menu has multiple options. But only 1st option works. It means, if I click on 2nd option, it does not work. If I change the sequence ( means 1st one will go to 2nd position, and 2nd one will come to 1st position ) then still 2nd one will not work.
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
switch(event->type())
{
case QEvent::ContextMenu:
{
foreach(QGraphicsItem* pItem, _scene->items())
{
if(pItem->isUnderMouse())
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
menu = new QMenu(this);
myMenu = menu->addMenu("Copy");
myMenu ->addAction(Name);
myMenu ->addAction(Address);
if(Name == menu->exec(mouseEvent->globalPos()))
{
// logic
}
if(Address == menu->exec(mouseEvent->globalPos()))
{
// logic
}
}
}
}
}
Always works only 1st mouse right click option. Why is so ?
The usual way to do something like this is to override the QGraphicsItem::mouseReleaseEvent() or QGraphicsItem::mousePressEvent() function of your item class.
This way, you won't have to do anything (no looping, etc...), it is already handled by the event loop.
Here you can find a simple example:
void MyItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
QMenu my_menu;
// Build your QMenu the way you want
my_menu.addAction(my_first_action);
my_menu.addAction(my_second_action);
//...
my_menu.exec(event->globalPos());
}
}
From the Qt documentation:
Note that all signals are emitted as usual. If you connect a QAction to a slot and call the menu's exec(), you get the result both via the signal-slot connection and in the return value of exec().
You just need to QObject::connect() the QActions you added to the context menu to the proper slots (here goes the "logic") and the job is done.
If you prefer to check the returned value by yourself, you just have to get the returned QAction* once and for all (only one call to QMenu::exec()) and branch on it.
For example:
void MyItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
QMenu my_menu;
// Build your QMenu the way you want
my_menu.addAction(my_first_action);
my_menu.addAction(my_second_action);
//...
QAction * triggered = my_menu.exec(event->globalPos());
if(triggered == my_first_action)
{
// Do something
}
else if(triggered == my_second_action)
{
// Do some other thing
}
//...
}
}
I would personnally prefer to stick with the signal-slot connections instead that manually handling the returned value, especially since each QAction is most likely to be already connected to its corresponding slot.

QKeyPress - Simulating key press in Qt

How can I simulate user interaction (key press event) in Qt?
I tried the same approach, but not able to get written on the lineEdit widget
ui->lineEdit->setFocus();
QKeyEvent *key_press = new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_X, Qt::NoModifier);
QApplication::sendEvent(ui->lineEdit, key_press);
Alternately
QApplication::postEvent(ui->lineEdit, key_press);
also didn't succeed.
I tried the below also and didn't get any result.
QKeyEvent key(QEvent::KeyPress, Qt::Key_X, Qt::NoModifier);
QApplication::sendEvent(ui->lineEdit, &key);
if (key.isAccepted()) {
qDebug()<<"everything is ok";
} else {
qDebug()<<"something wrong";
}
Please suggest what am I missing.
Regards,
Sayan
In the link you indicate an enter is given so the text is not necessary, but in the case you want to send a letter you must pass that parameter:
ui->lineEdit->setFocus();
QKeyEvent *key_press = new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_X, Qt::NoModifier, "X");
// text ─────┘
QApplication::sendEvent(ui->lineEdit, key_press);

Qt - Implementing QKeyEvent

How do I implement keyboard listening in Qt? I have the following setup that's not working. I have two classes, gameLogic and gameView. gameView has an instance of gameLogic:
gameView::gameView(QWidget *parent)
: QWidget(parent)
{
logic = new gameLogic(6);
logic->setFocusPolicy(Qt::TabFocus); //in one of the articles I read, this was supposed to fix the issue. It doesn't for me.
this->resize(1200, 700);
this->setStyleSheet("background-color: white");
QString str;
str.setNum(logic->n);
connect(logic, SIGNAL(playerStepped(int, int)), this, SLOT(movePlayer(int, int)));
}
And in gameLogic I am handling the keystrokes as follows:
void gameLogic::keybrdStep( QKeyEvent * keypressed )
{
if (keypressed->key() == Qt::Key_Q) {
_message = new QMessageBox;
_message->setText("Q");
_message->exec();
}
}
No matter how many times I push the button Q, nothing happens. What am I doing wrong? Which part am I missing? I'm on Linux Mint with the latest version of Qt.
void gameLogic::keybrdStep( QKeyEvent * keypressed )
Where did you get the method name "keybrdStep" from? Who do you think should call it?
The name of the method you need to override to get key presses is QWidget::keyPressEvent()

Qt How to highlight menu? [duplicate]

This question already has answers here:
How to focus menuBar() with Qt
(2 answers)
Closed 4 years ago.
I need to do hide/show main menu function by pressing the Alt key (like in FireFox). I know how to hide and show it, but I can't highlight menu after showing it. I've tried menuBar()->actions()[0].hover(), activateWindow(), setActiveAction(), activate(QAction::Hover), but nothing helps. How can I do it? Maybe I should use WinApi functions instead of Qt?
bool MainWindow::event(QEvent *event){
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *ke = static_cast<QKeyEvent*>(event);
if (ke->key() == Qt::Key_Alt)
{
keyReleaseEvent(ke);
return true;
}
}
return QMainWindow::event(event);
}
Handle Alt
void MainWindow::keyReleaseEvent (QKeyEvent* event)
{
if (event->key() == Qt::Key_Alt){
if (menuBar()->isHidden()){
menuBar()->show();
menuBar()->setFocus(Qt::MenuBarFocusEvent); //here I trying to highlight the menu
}
else{
menuBar()->hide();
{
}
}
Duplicate of How to focus menuBar() with Qt (which I just answered)
Because the answer isn't accepted on the other question, I can't flag this one as a duplicate. Including answer below as well:
I wanted to do the same thing. My solution, complete example, as a gist:
https://gist.github.com/xim/ee56564f425151ea2fa70f730d644873
As it contains a lot of other junk, a minimal example:
class AutoHidingMenuBar : public QMenuBar {
Q_OBJECT
public:
AutoHidingMenuBar() : QMenuBar() {
setMaximumHeight(0);
connect(qApp, &QApplication::focusChanged, this, &AutoHidingMenuBar::focusChanged);
}
private slots:
void focusChanged(QWidget *from, QWidget *to) {
bool inFocus = hasFocus() || isAncestorOf(focus) || hasFocusedChild();
if (inFocus && maximumHeight() == 0) {
auto action = activeAction();
setMaximumHeight(100);
if (action) {
// XXX This is a bit of a hack. We could do
// QCoreApplication::processEvents();
// setActiveAction(action);
// with almost the same effect, but then we *open* the first menu on single alt press...
auto evt = new QMouseEvent(QEvent::MouseMove, actionGeometry(action).center(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::postEvent(this, evt);
}
} else if (!inFocus && maximumHeight() != 0)) {
setMaximumHeight(0);
}
}
private:
bool hasFocusedChild();
};
Try to debug it and see what is happening.
You can do something like:
if (menuBar()->hasFocus())
qDebug() << " I have the focus";
else
qDebug() << " I don't have the focus";
And see if it has the focus but works bad or if it has no focus which I think that is problem... somewhere it is losing it
A little trick that may help you is using:
QTimer::singleShot(0, menuBar(), SLOT(setFocus()));
on your MainWindow::keyReleaseEvent
And last thing... Note that set focus needs a focusPolicy, maybe this helps you.

How to disable middle button functionality of QTextEdit?

I don't want mouse middle button to paste text in my QTextEdit. This code doesn't work. TextEdit inherits QTextEdit. After mouse middle button pastes it pastes copied text.
void TextEdit::mousePressEvent ( QMouseEvent * e ) {
if (e->button() == Qt::MidButton) {
e->accept();
return;
};
QTextEdit::mousePressEvent(e);
}
As mouse clicks are usually registered when the button is released, you should redefine the mouseReleaseEvent function.
You don't even need to redefine mousePressEvent, because the middle button isn't handled at all by that function.
I'm assuming you're using Linux here; right clicking in the window is likely to be triggering an insertion of mime data before you get to handle the mouse event, which is why it is still pasting text.
Therefore, according to Qt docs for paste: - " to modify what QTextEdit can paste and how it is being pasted, reimplement the virtual canInsertFromMimeData() and insertFromMimeData() functions."
I've been in the same case, that is to say: having parts of my CustomQTextEdit required to be non-editable.
As I truly love the middle mouse button paste feature, I did not wanted to disable it. So, here is the (more or less quick and dirty coded) workaround I used:
void QTextEditHighlighter::mouseReleaseEvent(QMouseEvent *e)
{
QString prev_text;
if (e->button() == Qt::MidButton) {
// Backup the text as it is before middle button click
prev_text = this->toPlainText();
// And let the paste operation occure...
// e->accept();
// return;
}
// !!!!
QTextEdit::mouseReleaseEvent(e);
// !!!!
if (e->button() == Qt::MidButton) {
/*
* Keep track of the editbale ranges (up to you).
* My way is a single one range inbetween the unique
* tags "//# BEGIN_EDIT" and "//# END_EDIT"...
*/
QRegExp begin_regexp = QRegExp("(^|\n)(\\s)*//# BEGIN_EDIT[^\n]*(?=\n|$)");
QRegExp end_regexp = QRegExp("(^|\n)(\\s)*//# END_EDIT[^\n]*(?=\n|$)");
QTextCursor from = QTextCursor(this->document());
from.movePosition(QTextCursor::Start);
QTextCursor cursor_begin = this->document()->find(begin_regexp, from);
QTextCursor cursor_end = this->document()->find(end_regexp, from);
cursor_begin.movePosition(QTextCursor::EndOfBlock);
cursor_end.movePosition(QTextCursor::StartOfBlock);
int begin_pos = cursor_begin.position();
int end_pos = cursor_end.position();
if (!(cursor_begin.isNull() || cursor_end.isNull())) {
// Deduce the insertion index by finding the position
// of the first character that changed between previous
// text and the current "after-paste" text
int insert_pos; //, end_insert_pos;
std::string s_cur = this->toPlainText().toStdString();
std::string s_prev = prev_text.toStdString();
int i_max = std::min(s_cur.length(), s_prev.length());
for (insert_pos=0; insert_pos < i_max; insert_pos++) {
if (s_cur[insert_pos] != s_prev[insert_pos])
break;
}
// If the insertion point is not in my editable area: just restore the
// text as it was before the paste occured
if (insert_pos < begin_pos+1 || insert_pos > end_pos) {
// Restore text (ghostly)
((MainWindow *)this->topLevelWidget())->disconnect(this, SIGNAL(textChanged()), ((MainWindow *)this->topLevelWidget()), SLOT(on_textEdit_CustomMacro_textChanged()));
this->setText(prev_text);
((MainWindow *)this->topLevelWidget())->connect(this, SIGNAL(textChanged()), ((MainWindow *)this->topLevelWidget()), SLOT(on_textEdit_CustomMacro_textChanged()));
}
}
}
}