Adding a shortcut to QAction inside QGraphicsScene context menu - c++

My QGraphicsScene subclass WorkspaceScene contains a variable for each action that it later uses inside the context menu. I have a function that sets up the actions and adds the shortcuts (which is called in the constructor of the class), and then I have a function that creates the context menu, which is called in the contextMenuEvent function I reimplemented.
Here's some relevant code:
// Constructor
WorkspaceScene::WorkspaceScene()
{
_setUpActions();
}
// ContextMenuEvent
void WorkspaceScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
_setUpItemMenu();
_itemContextMenu.exec(event->screenPos());
}
void WorkspaceScene::_setUpActions()
{
deleteAction = new QAction("Delete");
deleteAction->setShortcut(QKeySequence::Delete); // This should add the shortcut
connect(deleteAction, &QAction::triggered, this, &WorkspaceScene::deleteItemSlot);
}
void WorkspaceScene::_setUpItemMenu()
{
_itemContextMenu.clear();
_itemContextMenu.addAction(deleteAction);
}
This successfully sets up the actions and they work inside the context menu but the shortcut doesn't seem to work. What's the correct way of doing this?

I solved it by adding the QAction to the QGraphicsView parent of the QGraphicsScene.

Related

Qt: Pass QGraphicsSceneContextMenuEvent from QGraphicsView

I have derived from both QGraphicsView and QGraphicsRectItem. I overloaded the contextMenuEvent on both classes to provide popup menus. I want the QGraphicsView context menu when you click on white space the the QGraphicsItem popup menu when you click on an item.
At first implementation, I got the QGraphicsView popup no matter where I clicked. So I modified the contextMenuEvent as follows:
void CustomGraphicsView::contextMenuEvent(QContextMenuEvent* event)
{
if (QGraphicsItem *item = itemAt(event->pos())) {
MyRect* rect = dynamic_cast<MyRect*>(item);
QGraphicsSceneContextMenuEvent* context_event = dynamic_cast<QGraphicsSceneContextMenuEvent*>(event);
if (rect && context_event)
rect->contextMenuEvent(context_event);
}
else {
QMenu menu;
... create the QGraphicsView popup menu
}
}
The dynamic_cast for the QGraphicsSceneContextMenuEvent fails so I never call the contextMenuEvent for the rect. It won't compile if I just try to pass the event to the rect->contextMenu(), so I tried the cast.
What is the right way to do this?
This is a learning project to just create/move/rotate/delete 2D shapes using Qt. If someone wants to look at the whole thing, let me know.
OK, so I figured it out. Just make sure to pass the event through the base class method. Simple! This also works for the mousePressEvent(), mouseMoveEvent(), and mouseReleaseEvent functions.
void CustomGraphicsView::contextMenuEvent(QContextMenuEvent* event)
{
// if the event is on a GGraphicsItem just pass the event along
if (itemAt(event->pos())) {
QGraphicsView::contextMenuEvent(event);
}
else
{
QMenu menu;
... create popup for the CustomGraphicsView

Show/Hide of QMenu

I created the Start Menu by inheriting QMenu. I want to show and hide it using QPropertyAnimation in sliding style
Problem:
Show & hide are working fine when I call them explicitly(on click of start button). But when I click outside of start menu it hides instantly. Please suggest me what could be cause behind this:
My class is StartMenuUiClass which inherited from QMenu
mptrobj_animation is QPropertyAnimation object
void StartMenuUiClass::show()
{
this->raise();
disconnect(mptrobj_animation,SIGNAL(finished()),this,SLOT(this_hide()));
QMenu::show();
mptrobj_animation->setDuration(500);
mptrobj_animation->setStartValue(*mptrobj_startPosition);
mptrobj_animation->setEndValue(*mptrobj_endPosition);
mptrobj_animation->start();
}
void StartMenuUiClass::hide()
{
mptrobj_animation->setDuration(450);
mptrobj_animation->setStartValue(*mptrobj_endPosition);
mptrobj_animation->setEndValue(*mptrobj_startPosition);
connect(mptrobj_animation,SIGNAL(finished()),this,SLOT(this_hide()));
mptrobj_animation->start();
}
void StartMenuUiClass::this_hide()
{
this->lower();
emit work_Done();
QMenu::hide();
}
I think, if you click outside of your menu widget, it simply hides or closes without involving your StartMenuUiClass::hide() function. You can try to handle QMenu::hideEvent(QHideEvent *event) and/or QWidget::closeEvent(QCloseEvent *event). Something like this:
StartMenuUiClass::closeEvent(QCloseEvent *event) // the same for hideEvent()
{
this->hide();
event->accept();
}

Qt Run Single Application

I have a few dialogs and buttons that call these dialogs. However, every click on the button calls a new dialog window. I want the existing window to close first before the user can click on the button to open another.
Below is an example of a button calling a slot. Whenever I click on the button, it will call a copy of the dialog window. Is there any way to only call one copy of the dialog window only?
Thanks.
Bookmark.cpp:
Bookmark::Bookmark()
{
createButtons();
connect(bookmarkButton, SIGNAL(clicked()), this, SLOT(openBookmarkDlg()));
}
void Bookmark::createButtons()
{
...
bookmarkButton = new QToolButton;
bookmarkButton->setText("Bookmark");
addWidget(bookmarkButton);
...
}
void Bookmark::openBookmarkDlg()
{
BookmarkDlg *bkDlg = new BookmarkDlg;
bkDlg->show();
}
Bookmark.h:
class Bookmark : public QToolBar
{
Q_OBJECT
public:
Bookmark(void);
~Bookmark(void);
public slots:
void openBookmarkDlg();
private:
createButtons();
QToolButton *bookmarkButton;
};
If you want the dialog window to be modal, i.e. the application doesn't accept user input outside the dialog, use the window modality of the dialog.
Beware however that modal windows can be really annoying to users.
If BookmarkDlg inherits QDialog you can do the following:
void Bookmark::openBookmarkDlg()
{
BookmarkDlg *bkDlg = new BookmarkDlg;
prepareYourDialog(bkDlg);
/*If you expect to do something when the dialog is accepted*/
if (bkDlg->exec() == QDialog::Accepted)
{
/*Do something after dialog was accepted, and nothing when canceled*/
}
delete bkDlg;
}
Convert BookmarkDlg *bkDlg into a variable member of the class, instead of a local variable of the method:
private:
createButtons();
QToolButton *bookmarkButton;
BookmarkDlg *bkDlg;
Then on the implementation of the class, you could do:
void Bookmark::openBookmarkDlg()
{
if (!bkDlg)
bkDlg = new BookmarkDlg;
bkDlg->show();
}

Is there a way to know what activated QAction?

I have created instance of QAction inside QGraphicsView child class and connected it to my slot in the same class.
QAction *action = new QAction(tr("New"), this);
action->setObjectName("addStopAction");
action->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_N));
connect(action, SIGNAL(triggered()), this, SLOT(addNew()));
addAction(action);
Slot is a function creating new instance of QGraphicsItem on scene assigned to QGraphicsView.
void MyGraphicsView::addNew() {
// Insert new item at cursor position
}
I also add this action to a QMenu which serves as my class context menu.
QMenu *contextMenu = new QMenu(this);
contextMenu->addAction(action);
Everything works fine. When I press Command/Ctrl + N new item is created at cursor position. But when I right-click and select action from context menu I want new item to be created at menu positon.
I can, of course, do some little hack to flag if SLOT was called after contextMenuEvent or something like that, but what I would like to know is:
Is there any way to find out what made QAction emit its triggered() signal inside connected SLOT? That way I could handle when I should place new item at cursor position and when at context menu position inside SLOT implementation.
Of course, you can find out what signal emit inside connected SLOT.
Just use QObject::sender(). In you case:
void MyGraphicsView::addNew() {
QAction* pAction = qobject_cast<QAction*>(sender());
Q_ASSERT(pAction);
// do something with pAction
}
I think you can use custom data that a QAction object can contain.
You can set it when you create a context menu:
void showContextMenu(const QPoint &pos)
{
...
action->setData(pos);
...
}
And in the addNew() function you check if data exists and reset it in the end:
void addNew()
{
QPoint pos;
QPoint posFromAction = action->data()->toPoint();
if (posFromAction.isNull())
{
pos = QCursor::pos(); ///< pos will be current cursor's position
}
else
{
pos = posFromAction; ///< pos will be menu's position
}
doYourStuffAt(pos)
action->setData(QPoint()); ///< reset action's data
}
i managed something similar by connecting the menu to a function like connect (menu, SIGNAL( triggered(QAction*) ), this, SLOT( menuAction_triggered(QAction*) ));
when you execute you context menu, the QMenu::exec(QPoint) will return you the pointer to the action, so you may not need a extra function/slot for it.
you can check for the name of the action with its text QAction::text() or if you have stored your pointers somewhere by comparing the address.
soo long zai
You can reference self.sender() in the function that is called when by customContextMenuRequested signal.
self.tree1.customContextMenuRequested.connect(self.menu1pop) # rightclick menu signal
...
def menu1pop(self, pos):
widget = self.sender()
item = widget.itemAt(pos)
if item is None: return # only show contextmenu if on an item
self.setProperty("mywidget", widget) # pass current widget
self.menu1.popup(QCursor.pos()) # show menu at right click cursor position
self.sender() will return your widget object and then you can set the widget object inside a property.
Then when your action function is called you can recall the widget object by reading the property.
widget = self.property("mywidget")
It's a bit of a hack, but a simple and reliable way to know which widget your action was called from.
You MIGHT be able to use QObject::sender() in the slot that receives the call. Not tried this for actions though. It's probably a bit uglier than your proposed 'hack' (where you could actually implement that quite nicely with a scoped class).

QtMenubar Call Slot

This is inside a Menu class. The problem is addAction. This works, but there is no connection to slot:
QMenu* menu2 = new QMenu("Test");
menu2->addAction("Test");
When I do this:
QMenu* menu2 = new QMenu("Test");
menu2->addAction("Test", Menu, test);
I get compiler error: "error: expected primary-expression before ',' token"
I mean to call the test() function in the Menu class. What am I doing wrong?
Well, the error comes from passing Menu as an argument. You say Menu is a class, and classes are not expressions on themselves.
If you need to call test on an instance of Menu, where Menu is not a derivate from QObject (ie. no slots available), then you can just create a slot in the widget that contains the QMenu itself (probably a QMainWindow), and implement the call in there!
Edit: to add an example.
class MainWindow : public QMainWindow {
Q_OBJECT
// Usual declarations...
private slots:
void myCustomSlot();
};
Now, say that you're populating the main window inside its constructor:
MainWindow::MainWindow(...) {
// Some initialization code
QMenu *menu2 = new QMenu("Test");
menu2->addAction("Test", this, SLOT(myCustomSlot));
// Some more initialization code
}
// ...
void MainWindow::myCustomSlot() {
instanceOfMenu->test();
}
Of course, if you're creating the menu outside that class, you'd need to make the slot public, but that's another issue