问题
Greetings,
for one of my applications I'm trying to implement an "Edit" menu. This menu usually has the standard-entries Undo, Cut, Copy and Paste.
This menu is not there by default, and users seem to expect it especially on Mac OS X.
Is there a an easier way of implementing this, without doing so in every widget manually? Since most widgets have the copy/paste/undo mechanism already implemented via shortcuts, I'd like to provide a few simple menu actions that call them as well.
The actions should call whatever widget has the focus first, then they should pass the events upwards the object chain, I guess.
I'm using Qt 4.6 on Windows, Linux and Mac OS X.
Thanks!
回答1:
It's easy enough to accomplish half of the necessary functionality. Just create the Edit menu, along with the necessary QActions (copy/paste/undo/etc.) in your main window class and connect them to slots. In the slots, emulate the correct key press and release events (e.g. Ctrl+C for Copy) and send them to the currently focused widget. In code, something like this:
MainWindow::MainWindow(...)
{
...
connect( actionCopy, SIGNAL( triggered()), SLOT( copy()));
...
}
...
void MainWindow::copy()
{
QWidget* focused = QApplication::focusWidget();
if( focused != 0 )
{
QApplication::postEvent( focused,
new QKeyEvent( QEvent::KeyPress,
Qt::Key_C,
Qt::ControlModifier ));
QApplication::postEvent( focused,
new QKeyEvent( QEvent::KeyRelease,
Qt::Key_C,
Qt::ControlModifier ));
}
Of course, this is quite a hack. You need to modify the code for each target platform, changing the keyboard shortcuts to the correct ones, and it might happen that a widget that receives focus does something quiet unexpected with Ctrl+C. The worst downside in this method, in my opinion, is that you cannot properly control the enabled state of the Edit menu items. It is not possible to query from a generic widget whether a copy or paste operation would be possible or not.
I am unable to find a real solution to this problem - and would be surprised to find out that one exists - as the copy/paste functionality is generally hidden inside the class' code and not exposed through any standard set of signals/slots. After tonight's experiments with the functionality, I have decided to just forget having an Edit menu from my application and expect the users to know the keyboard shortcuts, or use the context sensitive menus.
回答2:
My impression is that the Edit menu applies to the central document widget, not loads of small ones. I have not tested, but if you have a form with QLineEdits, does the Edit menu (in the menu bar) really apply to that widget. Don't you simple bring up the context menu or press the short-cuts to access these options...
回答3:
user285740's solution didn't help me, since I'm using a browser control in my app (CEF or WebKit, doesn't matter).
Why? For browsers, focusWidget()
always seems to be NULL
, since <input>
elements are not widgets. I tried postEvent()
to other widgets - didn't work. Adding menu items with QAction::TextHeuristicRole
+ standard sequences like QKeySequence::Copy
didn't do the job, too (I could connect them only to my slots, not to standard slots). Cefclient sample loads a xib file, but it's not an option for me, since Qt creates everything from scratch.
Eventually, I found the solution! Create the same menu items from ObjectiveC++ code. They act like the ones created via QMenuBar
, but (!) they can be connected to some real automatic actions like @selector(copy:)
You can find an example here: nsMenuUtilsX::GetStandardEditMenuItem(), just do that from your ObjC++ code.
But, this code doesn't work if you execute it before QApplication::exec()
. Qt then "rewrites" the main menu programmatically... How to fix that? Well, maybe I'll add some hack like a QTimer. It doesn't do that if you don't try to add another items via QMenuBar. It's OK now! Qt-independent menu.
回答4:
The best solution I could find for this comes from https://www.qtcentre.org/threads/10709-using-cut()-copy()-paste(). In my app, this ended up looking like this:
connect(ui->actionCut, &QAction::triggered, []() {
QWidget *focusWidget = QApplication::focusWidget();
QLineEdit *lineEdit = dynamic_cast<QLineEdit*>(focusWidget);
QTextEdit *textEdit = dynamic_cast<QTextEdit*>(focusWidget);
if (lineEdit && lineEdit->isEnabled() && !lineEdit->isReadOnly())
lineEdit->cut();
else if (textEdit && textEdit->isEnabled() && !textEdit->isReadOnly())
textEdit->cut();
});
This is really awful, and has to be done for each of the standard menu items (undo, redo, cut, copy, paste, delete, select all...), and getting the menu items to enable/disable correctly requires yet more hoops to be jumped through. This is the first time, in porting a Cocoa app to Qt, that I have felt that Qt is clearly and markedly inferior (in this case, to Cocoa's first responder mechanism, which doesn't seem to exist in Qt at all). Still, I think it's better than the solution proposed by user285740, which hard-codes particular keyboard actions. YMMV.
来源:https://stackoverflow.com/questions/2047456/how-to-implement-the-edit-menu-with-undo-cut-paste-and-copy