Context Menu works for child but not parent Widget - c++

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.

Related

QT Window Modality disables actions in menu

I am developing a desktop application on macOS. I have a class that is a subclass of QMainWindow. Inside this window there are many dockwidgets. I need to set WindowModality to WindowModal, so user cannot interact with other opened windows. But my window has a menubar with many menus that have some QActions inside and when I setWindowModality(Qt::WindowModal) it automatically disables every action in menu and I need them to be enabled.
Can anybody provide some simple solution for this? Or it is not possible?
Thank you very much.
EDIT:
I have many windows in my application. I have a real main window from which you can open another window and also from that window another window. And here is the situation where i need my child windows to be modal. But they also have their own menubar which gets automatically disabled when window modality is turned on. Ive been googling it already maybe for 10 hours without any solution. I cannot test it but I guess that on windows the menubar would not disable because the native menu is quite different.
If there is no specific need of using QWindow, then it'll be easier to achive this using QDialog class instead. Using QDialog you can simply show dialog as modal using exec() method.
EDIT: Basically, you can add QMenuBar element to every QLayout class object using QLayout::setMenuBar method.
If you want to add menu bar to QDialog, you've got to set layout for your dialog, then programatically create desired QMenuBar object and pass it to QDialog layout (which you can access using QDialog::layout method). Simple example below:
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
QMenuBar* menu = new QMenuBar();
QMenu* fileMenu = menu->addMenu("File"); //Create 'File' menu inside menu bar
QAction* closeAction = fileMenu->addAction("Close"); //Create 'Close' action in 'File' menu
connect(closeAction, QAction::triggered, this, close); //Close dialog after triggering 'Close' action
layout()->setMenuBar(menu); //Add menu bar to QDialog layout
}
Please consider the usage of Qt::ApplicationModal.
This keeps the modality but gives you other behaviour on MAC.

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.

Reusing a QMenu within multiple Qmenu

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);
}
}

QDockWidget is closed if main window is minimized

I'm using Qt 4.7 on Windows 7 Ultimate 32 bit.
The QMainWindow of my program has a QDockWidget. I've noticed that if I minimize the main window by the minimize button on the title bar, after restoring it the dock widget is closed. I didn't write any support for a feature like this!
How does this happen and how to prevent this?
Thanks.
I encountered this error when writing my own application. I have QDockWidget with options for my application. Using Qt Creator I created menu with QAction actionMenu which was checkable. Then I connected QDockWidget and QAction like this:
QObject::connect(ui->dockWidget, SIGNAL(visibilityChanged(bool)),
ui->actionMenu, SLOT(setChecked(bool)));
QObject::connect(ui->actionMenu, SIGNAL(toggled(bool)),
ui->dockWidget, SLOT(setVisible(bool)));
The order of connection is not important. And then when I minimized application with QDockWidget being visible, after I restored it QDockWidget was closed and actionMenu was unchecked.
In fact there are two solutions. First which works for me is to use SIGNAL(triggered(bool)) instead of SIGNAL(toggled(bool)):
QObject::connect(ui->dockWidget, SIGNAL(visibilityChanged(bool)),
ui->actionMenu, SLOT(setChecked(bool)));
QObject::connect(ui->actionMenu, SIGNAL(triggered(bool)),
ui->dockWidget, SLOT(setVisible(bool)));
The second solution uses action which you can obtain from QDockWidget:
// Retrieve action from QDockWidget.
QAction *action = ui->dockWidget->toggleViewAction();
// Adjust any parameter you want.
action->setText(QString("&Menu"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
action->setStatusTip(QString("Press to show/hide menu widget."));
action->setChecked(true);
// Install action in the menu.
ui->menuOptions->addAction(action);
I know for sure that SIGNAL(toggled(bool)) was causing in my application closure of QDockWidget.
I faced the same problem... I managed to get rid of it by using a method called StoreWindowsLayout and RestoreWindowsLayout.
StoreWindowsLayout will save the content of the ByteArray returned by the Method QMainwindow::saveState().
RestoreWindowsLayout will restore that bytearray, and therefore your windows layout, the qdockwidget visibility state and so on...
I call StoreWindowsLayout on ApplicationMainFrm::changeEvent, on ApplicationMainFrm::closeEvent (it's likely this one you'll need) and in ApplicationMainFrm::hide().
Then I use restoreWindowsLayout in ApplicationMainFrm::showEvent.
Exemple of use of restoreWindowsLayout in my MainForm :
void ApplicationMainFrm::showEvent(QShowEvent* pEvent)
{
QMainWindow::showEvent(pEvent);
restoreWindowsLayout();
}
Hope it helps !

Putting a close button on QTabWidget

I'm using a QTabWidget to render multiple documents in a window, and I want to draw a close button on each tab. I'm using Vista and Qt4, so the tab widget is a native windows control; this may affect the feasibility.
Does anyone know if it is possible to do this using the QTabWidget control, or do I have to create a custom widget? If creating a new widget is the only option, any pointers would be much appreciated; I'm relatively new to Qt.
Since Qt 4.5. If you just call setTabsClosable(true) on QTabWidget, you will have the close buttons but they won't be bound to an action.
You have to connect the tabCloseRequested(int) signal to one of your own slots if you want the buttons to do something.
MainWindow::MainWindow()
m_tabs = new QTabWidget();
m_tabs->setTabsClosable(true);
connect(m_tabs, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
void MainWindow::closeTab(const int& index)
{
if (index == -1) {
return;
}
QWidget* tabItem = m_tabs->widget(index);
// Removes the tab at position index from this stack of widgets.
// The page widget itself is not deleted.
m_tabs->removeTab(index);
delete(tabItem);
tabItem = nullptr;
}
In 4.5 there is function
void setTabsClosable ( bool closeable )
Currently there is no way to do this with the stock QTabWidget, however the upcoming Qt 4.5 (planned to be released in March 2009) will have the ability to add close buttons to tabs either manually or by setting a QTabBar.TabsClosable property.
Until then, the only way to get close buttons is to subclass QTabWidget or QTabBar and add it manually (possible, but not trivial).