PDA

View Full Version : Cloning a QAction object



brazso
30th December 2008, 16:19
Is it possible to clone a QAction object? I would like to reuse a QAction element named actionTrack_Analysis in my example, but its parent object must be altered, so I should like to make a copy of it. For the time being I use this messy code, the important properties of the object are copied manually:


QAction action = contextMenu.addAction(actionTrack_Analysis->text(), this, SLOT(on_actionTrack_Analysis_triggered()), actionTrack_Analysis->shortcut());
action->setEnabled(actionTrack_Analysis->isEnabled());
action->setIcon(actionTrack_Analysis->icon());
contextMenu.addAction(action);

wysota
31st December 2008, 02:19
Why do you need to change the parent? It's not possible to copy the action but you can connect one action to another's triggered() signal. You can even connect to its changed() signal to update one action when the other changes (for instance it gets disabled).

So you can implement a clone action with more or less this:

class CloneAction : public QAction {
Q_OBJECT
public:
CloneAction(QAction *original, QObject *parent = 0) : QAction(parent){
connect(original, SIGNAL(changed()), this, SLOT(updateMe())); // update on change
connect(original, SIGNAL(destroyed()), this, SLOT(deleteLater())); // delete on destroyed
connect(original, SIGNAL(triggered()), this, SLOT(trigger())); // trigger on triggered
// repeat with toggled, etc.
// connect the other way round as well if it makes sense - depends if you want one way or two way relation
m_orig = original;
}
private slots:
void updateMe(){
static QStringList props = QStringList() << "text" << "iconText" << "enabled" << ...;
foreach(const QString prop, props)
setProperty(qPrintable(prop), m_orig->property(qPrintable(prop)));
}
private:
QAction *m_orig;
};

You can build upon it...

brazso
5th January 2009, 16:38
Thanks for your reply. I'm a beginner in QT, I think it is better to describe my original problem. I have an action which is defined in the main menu (created in QTDesigner), but it can also be used from a toolbar and from a context menu of a control. I wish to know that my action is fired from the context menu (or not) because in this case the slot function connected to the action should be worked slightly differently. In fact I do not wish to create a new action element, I prefer using the existing one, but in this case the sender()->parent() is always the MainWindow object in the connected slot function. Therefore I'm forced to make a duplicate of the original action, it must be created in my context menu. Now I can use sender()->parent()->objectName() to distinguish the source object but it's not an elegant solution, at least copying properties is ugly.

In the example below, actionTrack_Analysis is the original action. Simple addAction(QAction *) is commented out, I have to duplicate the original action and only its important properties are copied.



QMenu contextMenu(this);
contextMenu.setObjectName("contextMenu");

// unfortunately original actions cannot be used, because we must know
// that the sender object is from contextMenu or MainWindow

//contextMenu.addAction(actionTrack_Analysis);
QAction* action = contextMenu.addAction(actionTrack_Analysis->text(), this, SLOT(on_actionTrack_Analysis_triggered()), actionTrack_Analysis->shortcut());
action->setEnabled(actionTrack_Analysis->isEnabled());
action->setIcon(actionTrack_Analysis->icon());
contextMenu.addAction(action);

wysota
5th January 2009, 20:28
Thanks for your reply. I'm a beginner in QT, I think it is better to describe my original problem. I have an action which is defined in the main menu (created in QTDesigner), but it can also be used from a toolbar and from a context menu of a control. I wish to know that my action is fired from the context menu (or not) because in this case the slot function connected to the action should be worked slightly differently. In fact I do not wish to create a new action element, I prefer using the existing one, but in this case the sender()->parent() is always the MainWindow object in the connected slot function.

If these "actions" do different things (even if they don't differ much), I'd use separate QAction instances.

Checking if an action was fired from a context menu is relatively easy - you can set a temporary value on the action while building the menu and remove it after the menu is closed, like in the snippet below, but I'd still use separate actions.


QAction *someAction = ...;
connect(someAction, SIGNAL(triggered()), ..., SLOT(doSomething()));
//...
QMenu menu;
menu.addAction(someAction);
someAction->setProperty("calledFromContextMenu", true);
//...
menu.exec();
someAction->setProperty("calledFromContextMenu", false);

//...

void X::doSomething(){
QAction *a = qobject_cast<QAction*>(sender());
if(!a) return;
if(a->property("calledFromContextMenu").toBool()){
// ...
} else {
// ...
}
}