Reusing a QMenu within multiple Qmenu - c++

Hi all,
I have some code generating a dynamically filled QMenu depending on some data (I will call it thisMenu). The QMenu thisMenu is created, taking some "main" QWidget as parent and is added to a QMenuBar within this QWidget (menuBar.addMenu(&thisMenu). Latter on, I want the user to be able of accessing thisMenu from a context menu (the user right click on some portion of the QWidget, which pops a QMenu (called contextMenu) with some actions, and the previous QMenu as a sub-menu).
If I reuse the QMenu that I first created with contextMenu.addMenu(&thisMenu) I find out that, even if contextMenu pops at the right global position, thisMenu is always translated to some other position and appearing sometimes above, sometimes under contextMenu.
I can test that this is linked to the parenting chain : thisMenu is not a child of contextMenu, if I create it a child of contextMenu, everything is fine. Is there a way of cleanly handling this case without recreating a QMenu similar to thisMenu, or changing the parent of thisMenu; i.e. reusing thisMenu in both QMenuBar and in some context menu/QMenu? In other what is the proper way of handling parenting chain for QMenu and sharing QMenu?
Thank you,

In other what is the proper way of handling parenting chain for QMenu and sharing QMenu?
You cannot share a QMenu across multiple places -- each QMenu can only exist in one place at a time. You should create separate QMenus: One for your menu bar and one for your context menu.
A simple way is to put your menu-generating code in a for-loop, to create multiple identical menus.
May I ask why you want to reuse your QMenu?
I can test that this is linked to the parenting chain : thisMenu is not a child of contextMenu
Yes, that is described in the documentation. When you add one QMenu to another, the parent doesn't change: http://qt-project.org/doc/qt-5/QMenu.html#addMenu
if I create it a child of contextMenu, everything is fine.
The position of a widget is always painted in a position relative to its parent. (Remember: A QMenu is a QWidget)

Following JKSH's answer, I decided to use a function to duplicate QMenu, without duplicating the QAction in it (they are not inheriting QWidget), hence conserving all established connections :
void duplicateMenu(QMenu* dst, QMenu& origin)
{
QMenu* sub = dst->addMenu(origin.title());
QList<QAction*> actions=origin.actions();
for(QList<QAction*>::iterator it=actions.begin(); it!=actions.end(); it++)
{
QMenu* itMenu = (*it)->menu();
if(itMenu!=NULL)
duplicateMenu(sub, *itMenu);
else
sub->addAction(*it);
}
}

Related

How do I set the parent of a widget after the widget has already been initlized (ex. if the widget is a parameter to a function call)?

I know that usually parents are set by being given in the constructor argument, for example if you wanted to have a push button within a frame you could do:
QFrame frame = new QFrame();
QPushButton* button = new QPushButton(frame);
But I already have the push button that I want to add as a child to a frame (it was received as an argument in the call to the function I'm working on).
Is there any way to add an already existing widget as a child to another widget? Or add a new child to an existing or nonexistent parent?
(Disclaimer: Answer blatantly stolen from G.M.'s comment (with permission - see above ;))
Simply use QWidget's setParent(QWidget *parent) method.
Note: The new parent takes ownership of the QWidget (i.e. deallocating it when the parent is destroyed etc.) and you will need to call show() on the widget again to make it visible, as changing parents turns QWidgets invisible.

Qt5 QMainWindow components deletion

One of our teacher asked us to create a Qt application without any UI file for the main window (a QMainWindow). Usually I always create one, leave it empty and let the uic deal with it.
I know that if a parental relation is defined between a widget (child) and its parent, then there is no need to delete the widget (deleted when the parent is deleted). So, when the UI is deleted, all the children are destroyed.
If we do not use an UI file (not generated), do we have to manually delete all the widget added to the GUI?
A little sample:
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent){
layout = new QHBoxLayout(this);
aButton = new QButton(this);
layout->addWidget(aButton);
...
}
MainWindow::~MainWindow(){
delete ui; // No need to delete more if parental relation.
// However, what do we do if no ui has been generated?
// Do we have to delete aButton?
}
The value of parent is 0. It is the main entry of the application.
Thanks
Please refer to this article
QWidget, the fundamental class of the Qt Widgets module, extends the parent-child relationship. A child normally also becomes a child widget, i.e. it is displayed in its parent's coordinate system and is graphically clipped by its parent's boundaries. For example, when the application deletes a message box after it has been closed, the message box's buttons and label are also deleted, just as we'd want, because the buttons and label are children of the message box.
So, there is no difference do you use ui or not. When you delete window, all its children will be deleted too.

Dragging QDockWidgets between QMainWindows

I have a question regarding dragging QDockWidgets between QMainWindows. I have an application with different "DockAreas" which have a QMainWindow member with one or more QDockWidgets. Additionally, I want to make QDockWidgets drag-and-droppable into other, already floating QDockWidgets (not tabbed!).
To have the nice hover-effect upon drag-and-drop, the floating QDockWidgets are embedded in a new instance of my DockArea with the QMainWindow member. Now I intercept the mousemove event of the dragged QDockWidget and change its parent when it hovers over another QMainWindow of my DockArea. Unfortunately, this causes a crash and it looks like the original QLayoutItem, where this QDockWidget was in, is gone.
void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
{
// ...
// now this widgetItem member of state is kaputt
layout->hover(state->widgetItem, globalMousePos);
}
I found different suggestions for dragging-dropping QDockWidgets between QMainWindows on the internet:
1) Re-parenting - that's what I am trying atm
2) QDrag - did not work for me, async exec() breaks my application and at the end I have to reparent anyway
3) Call addDockWidget(...) of the new QMainWindow - docks the QDockWidget and I would have to make it floating again and somehow "continue the dragging"
I am open to any new suggestions :) Thank you in advance!
I'm wrestling with the same Problem right now and want to share.
Ich found this forum post dealing with the same problem: http://www.qtcentre.org/threads/41847-Dragging-QDockWidgets-between-QMainWindows
This solution is however flawed: it doesn't work properly if you allow the Dockables to be floating. And the enterEvent does not actually trigger when you enter the other window and still dragging the QDockWidget. It only triggers after release.
I'm also going for the reparenting (1). QDrag seems problematic when you also want to allow rearranging the QDockWidget within the Window.
I think one would correctly do this by reacting to the drop/release of the dragged QDockWidget. But I can't find an event or signal for this.

Context Menu works for child but not parent Widget

I'm developing a desktop program that displays data in several QWidget windows, and I'm attempting to use a context menu to allow the user to copy/save an image of the window for use elsewhere. I encounter a pretty strange error when trying to get the context menu to appear in the window. I initially used the Qt Design mode to create the on_Plot_customContextMenuRequested(const QPoint &pos) slot for the entire window (entire Qwidget?), which did not work. When I create the on_SignalPlot_customContextMenuRequested slot, it works perfectly, but only on that specific widget, which is a subset of the entire window. I use identical code for each slot, and the debug output shows that the individual widget context menu request signal is emitted but the signal for the whole window is not. Is there a way to get it to work for the whole window?
Could the fact that the two child widgets take up the entire window cause the issue? I use a grid layout to ensure that the plots resize with the window.
(I'd show an image of the designer layout, but I don't have enough reputation.)
Does not work (code for whole window):
void Plot::on_Plot_customContextMenuRequested(const QPoint &pos)
{
qDebug()<<"plot context menu requested";
qDebug()<<pos;
QMenu* menu=new QMenu();
menu->addAction(copyWinAct);
menu->addAction(saveWinAct);
menu->exec(QCursor::pos());
}
Works Perfectly (code for individual plot/widget):
void Plot::on_SignalPlot_customContextMenuRequested(const QPoint &pos)
{
qDebug()<<"plot context menu requested";
qDebug()<<pos;
QMenu* menu=new QMenu();
menu->addAction(copyWinAct);
menu->addAction(saveWinAct);
menu->exec(QCursor::pos());
}
Thanks for your help.
If anyone else has this problem, I've found a solution. By connecting the customContextMenuRequested signal to one slot, each sub-widget will display the same context menu. Setting the overall widget to the same slot will make the whole window behave in the same manner. I added the following code to the class default constructor and created the corresponding slot to get everything to behave properly.
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(contextMenuSlot(QPoint)));
QList<QWidget *> windowChildren=this->findChildren<QWidget *>();
foreach (QWidget *child, windowChildren)
{
child->setContextMenuPolicy(Qt::CustomContextMenu);
connect(child,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(contextMenuSlot(QPoint)));
}
Good luck to anyone else fighting this problem.

Change text in a QAction that's inside of a QMenu

I have a QMenu objects to which I've added several QAction objects with a certain text. All works well, except that I would like to change the text of those QActions at runtime. I've looked at the QMenu API and didn't find any way to to get them. I also tried actions.at(x) and a few other things. What's the right way to do this?
It really depends on how you are structuring your app. In some circumstances you might be saving your QAction's as members, to which you could access directly. Or you might just be saving the QMenu and populating it with QAction's. Either way, once you have your action, you just call setText(QString) on it:
// init
menu = new QMenu(this);
...
menu->addAction("foo");
// later on
QAction *action = menu->actions().at(0); // access just the first QAction
action->setText("bar");
QMenu also allows you to look up actions by a QPoint location. Again, it really depends on exactly how you will be determining which action you want to change.