How to override Paste or catch the moment before Paste happens in QLineEdit?
-
I need to change the behavior of "Paste" (completely override, make it act in a different way) in the standard context menu of the
QLineEdit
.
Or at least somehow catch the moment before Paste is about to happen inQLineEdit
and do some preparations (clean the text line).
How can I do it for both the context menu and shortcut ctr+v?I have something like this:
MyLineEdit::MyLineEdit(QWidget* parent) : QLineEdit(parent) { setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &MyLineEdit::customContextMenuRequested, this, &MyLineEdit::ShowContextMenu); } void MyLineEdit::ShowContextMenu(const QPoint& pos) { QScopedPointer<QMenu> menu(createStandardContextMenu()); QAction* defaultPaste = menu->actions()[5]; // How can I override default Paste functionality here? // Or at least add some functionality before QLineEdit::paste() slot will be executed? menu->exec(mapToGlobal(pos)); }
It's not necessary to use this code/this way tho. Any better/proper way is appreciated.
@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
catch the moment before Paste is about to happen in QLineEdit and do some preparations
The problem is that it is not done within
QLineEdit
class.See here
TheQLineEdit::paste()
just triggers the internal paste function (here), which takes the content from clipboard and inserts it to your widget. And because it's internal, you cant just access or intercept it without changing the Qt source.You could try to grab the
keyEvent
and check if the pressed key(-sequence) isQKeySequence::Paste
(https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff. -
Isn't there a way to do something with
QLineEdit::paste()
slot?You could try to grab the keyEvent and check if the pressed key(-sequence) is QKeySequence::Paste (https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.
But what about context menu Paste action?
It'll change shortcut behavior but if you'll click Paste in the context menu it'll be still the same. -
Isn't there a way to do something with
QLineEdit::paste()
slot?You could try to grab the keyEvent and check if the pressed key(-sequence) is QKeySequence::Paste (https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.
But what about context menu Paste action?
It'll change shortcut behavior but if you'll click Paste in the context menu it'll be still the same.@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
It'll change shortcut behavior but if you'll click Paste in the context menu it'll be still the same.
You have access to your context menu and its
QActions
, dont you?!There you can add a connection to do your custom stuff before you call the
QLineEdit::paste()
slot.Maybe you have to disconnect everything from your standard
Paste-QAction
first, because I don't know if the connection is made, when creating the "standard context menu". Then the paste would happen before you can do your stuff. So delete the default connection, connect to your slot and then callpaste()
when you are ready to insert the text.->
defaultPaste->disconnect()
// Disconnects all slots from object@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
menu->actions()[5]
Make sure that
5
is always the paste action. The order might change, esp. when you add moreQActions
to your menu. -
So delete the default connection, connect to your slot and then call
paste()
when you are ready to insert the text.Oh, this does the trick!
So I do this for the context menu and I also should do what you said earlier aboutkeyEvent
to override whole paste functionality?
You either ctrl+v or click Paste in the context menu, there's nothing else I should be worried about?
There's no other way to paste text inQLineEdit
?Make sure that
5
is always the paste action. The order might change, esp. when you add moreQActions
to your menu.I'd really like to use
findChild<QAction*>("action_name")
but objectName's are not set for created actions: https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlineedit.cpp.html#2210 -
So delete the default connection, connect to your slot and then call
paste()
when you are ready to insert the text.Oh, this does the trick!
So I do this for the context menu and I also should do what you said earlier aboutkeyEvent
to override whole paste functionality?
You either ctrl+v or click Paste in the context menu, there's nothing else I should be worried about?
There's no other way to paste text inQLineEdit
?Make sure that
5
is always the paste action. The order might change, esp. when you add moreQActions
to your menu.I'd really like to use
findChild<QAction*>("action_name")
but objectName's are not set for created actions: https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlineedit.cpp.html#2210@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
You either ctrl+v or click Paste in the context menu, there's nothing else I should be worried about?
How else you want to paste from clipboard? I only know about keyboard shortcuts (
Ctrl + V
orInsert
key) and menus.@Pl45m4 said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
because I don't know if the connection is made, when creating the "standard context menu"
I can confirm that now :)
(createStandardContextMenu()
Code)@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
I'd really like to use findChild<QAction*>("action_name") but objectName's are not set for created actions:
I guess you can work with the shortcut... (unless you don't change it manually!)
action->shortcut()
returns aQKeySequence
. And you know it has to beQKeySequence::Paste
You only need to check which the primary KeySequence is (whatshortcut()
returns... Ctrl + V or "Paste" or whatever), sinceshortcut()
returns the primary only.
Otherwise you could useshortcuts()
, but this will return a list with all shortcuts for this widget... so you would have to check and compare even more... -
@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
catch the moment before Paste is about to happen in QLineEdit and do some preparations
The problem is that it is not done within
QLineEdit
class.See here
TheQLineEdit::paste()
just triggers the internal paste function (here), which takes the content from clipboard and inserts it to your widget. And because it's internal, you cant just access or intercept it without changing the Qt source.You could try to grab the
keyEvent
and check if the pressed key(-sequence) isQKeySequence::Paste
(https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.@Pl45m4 said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
You could try to grab the
keyEvent
and check if the pressed key(-sequence) isQKeySequence::Paste
(https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.Did you mean something like this?
It works but feels so wrong.MyLineEdit::MyLineEdit(QWidget * parent) : QLineEdit(parent) { setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &MyLineEdit::customContextMenuRequested, this, &MyLineEdit::ShowContextMenu); QShortcut* shortcut = new QShortcut(QKeySequence::Paste, this); // It works only with these two lines. qApp->installEventFilter(this); // And I've no idea how it works. } void MyLineEdit::MyPaste() { clear(); paste(); } void MyLineEdit::ShowContextMenu(const QPoint& pos) { QScopedPointer<QMenu> menu(createStandardContextMenu()); QAction* defaultPaste = menu->actions()[5]; defaultPaste->disconnect(); // Remove connect(action, SIGNAL(triggered()), SLOT(paste())); // Because of QShortcut* shortcut = new QShortcut(QKeySequence::Paste, this); in constructor // Paste doesn't have shortcut in the context menu, setting it to default one again defaultPaste->setShortcut(QKeySequence::Paste); connect(defaultPaste, &QAction::triggered, [this]() { MyPaste(); // Override the context menu Paste's functionality }); menu->exec(mapToGlobal(pos)); } bool MyLineEdit::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::ShortcutOverride) { QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); if (keyEvent->matches(QKeySequence::Paste)) { MyPaste(); // Override Ctrl+V functionality return true; } } return QLineEdit::eventFilter(watched, event); }
Especially these two lines:
QShortcut* shortcut = new QShortcut(QKeySequence::Paste, this); // It works only with these two lines. qApp->installEventFilter(this); // And I've no idea how it works.
It doesn't want to work without creating a new
QShortcut
.Am I doing something wrong?
-
@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
Am I doing something wrong?
You install the event filter on
qApp
. This is not a good idea. For all widgets this oneMyLineEdit
will interceptCtrl+V
. Just try having two line edits and you probably will see some weird behavior.If you want to use
eventFilter()
(which is a reasonable choice), you should make a separate class (derived fromQObject
) just for event filtering and install it on a singleQLineEdit
, or ratherMyLineEdit
in your case. The reason for this is that the filter should only apply to this single widget and not everyone else.There's no other way to paste text in
QLineEdit
?I am not entirely sure. But I suspect that there is also some drag'n'drop behavior here. At least there should be dragging and dropping within the line edit itself. Furthermore, you might be able to drop text from anywhere else.
-
@Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
Am I doing something wrong?
You install the event filter on
qApp
. This is not a good idea. For all widgets this oneMyLineEdit
will interceptCtrl+V
. Just try having two line edits and you probably will see some weird behavior.If you want to use
eventFilter()
(which is a reasonable choice), you should make a separate class (derived fromQObject
) just for event filtering and install it on a singleQLineEdit
, or ratherMyLineEdit
in your case. The reason for this is that the filter should only apply to this single widget and not everyone else.There's no other way to paste text in
QLineEdit
?I am not entirely sure. But I suspect that there is also some drag'n'drop behavior here. At least there should be dragging and dropping within the line edit itself. Furthermore, you might be able to drop text from anywhere else.
@SimonSchroeder said in How to override Paste or catch the moment before Paste happens in QLineEdit?:
But I suspect that there is also some drag'n'drop behavior here
Ah yeah, forgot about that.
-
This works and looks better than
eventFilter
.MyLineEdit::MyLineEdit(QWidget * parent) : QLineEdit(parent) { setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &MyLineEdit::customContextMenuRequested, this, &MyLineEdit::ShowContextMenu); } void MyLineEdit::MyPaste() { clear(); paste(); } void MyLineEdit::ShowContextMenu(const QPoint& pos) { QScopedPointer<QMenu> menu(createStandardContextMenu()); const auto actions = menu->actions(); const QString translatedActionName = QCoreApplication::translate("QLineEdit", "&Paste"); const auto it = std::find_if(actions.cbegin(), actions.cend(), [&translatedActionName](const QAction * action) { return action->text().startsWith(translatedActionName); }); if (it != actions.cend()) { const auto actionPaste = *it; actionPaste->disconnect(SIGNAL(triggered())); connect(actionPaste, &QAction::triggered, this, &MyLineEdit::MyPaste); // Override the context menu's Paste functionality } menu->exec(mapToGlobal(pos)); } void MyLineEdit::keyPressEvent(QKeyEvent *event) { if (event->matches(QKeySequence::Paste)) { MyPaste(); // Override Ctrl+V functionality event->accept(); return; } QLineEdit::keyPressEvent(event); }
-
I came across this post, which helped me in the right direction.
But instead of using the
CustomContextMenu
method, I would simply override the virtualcontextMenuEvent (QContextMenuEvent *event)
function.Secondly, finding + disconnect the paste-action can be much easier and cleaner.
void MyLineEdit::MyPaste() { clear(); paste(); } void MyLineEdit::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = createStandardContextMenu(); const QList<QAction*> actions = menu->actions(); foreach (QAction * action, actions) { // disconnect returns true if it successfully disconnected the slot "paste()". // Thus disconnect fails for all non-"paste" actions :-) if (action->disconnect(this, SLOT(paste()))) { connect(action, &QAction::triggered, this, &MyLineEdit::MyPaste); // Override the context menu's Paste functionality break; } } menu->setAttribute(Qt::WA_DeleteOnClose); menu->popup(event->globalPos()); } void MyLineEdit::keyPressEvent(QKeyEvent *event) { if (event->matches(QKeySequence::Paste)) { MyPaste(); // Override Ctrl+V functionality event->accept(); return; } QLineEdit::keyPressEvent(event); }