tabifyDockWidget() adding unknown widgets
-
wrote on 30 Dec 2019, 20:43 last edited by Julieng
Hello,
I am fighting against an issue for several days without understanding the issue. Even if I delete QDockWidgets, the number of widgets is always increasing. Could you help me ? Thanks in advance !
I have a vector of Qt panels stored in a vector:
myWindow { public: void rebuildContent(); private: std::vector<uncloseableDockWidget *> m_widgets; }
uncloseableDockWidget is an interface impeding QDockWidgets to be closed.
class uncloseableDockWidget : public QDockWidget { Q_OBJECT public: explicit uncloseableDockWidget(const QString & title, QWidget * parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()) : QDockWidget(title, parent, flags) { setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); setAllowedAreas(Qt::AllDockWidgetAreas); } virtual ~uncloseableDockWidget() override { } public slots: virtual void updateView() = 0; protected: void closeEvent(QCloseEvent * event) override { event->ignore(); } };
Now, coming back to my main window, when calling rebuildContent, I clear the QDockWidgets and build new ones:
void myWindow::rebuildContent() { std::cout << "test1 " << QApplication::allWidgets().size() << std::endl; // Remove already existing dock widgets for(uncloseableDockWidget * w:m_widgets) { m_container->removeDockWidget(w); delete w; } m_widgets.clear(); std::cout << "test2 " << QApplication::allWidgets().size() << std::endl; // Add new widgets m_widgets.emplace_back(new customDockWidget(tr("widget1"))); m_widgets.emplace_back(new customDockWidget(tr("widget2"))); // Connect signals std::cout << "test3 " << QApplication::allWidgets().size() << std::endl; for(uncloseableDockWidget * w:m_widgets) connect(w, SIGNAL(updateViewRequest()), this, SLOT(updateView())); std::cout << "test4 " << QApplication::allWidgets().size() << std::endl; // Add and tabify dock widgets for(uncloseableDockWidget * w:m_widgets) m_container->addDockWidget(Qt::RightDockWidgetArea, w); std::cout << "test5 " << QApplication::allWidgets().size() << std::endl; for(unsigned int i=1; i<m_widgets.size(); i++) m_container->tabifyDockWidget(m_widgets[i-1], m_widgets[i]); std::cout << "test6 " << QApplication::allWidgets().size() << std::endl; if(m_listOfDockWidgets.size()) m_widgets.front()->raise(); std::cout << "test7 " << QApplication::allWidgets().size() << std::endl; }
When I run function 'rebuildContent' twice, here is the result:
test1 305 test2 305 -> no widget in m_widgets for first call test3 365 -> build 60 widgets test4 365 -> nothing done (just connecting things) test5 365 -> nothing done (adding QDockWidgets) test6 368 -> I do not understand why 3 widgets are created when tabifying test7 368 -> nothing done (just raising tab) --- test1 368 -> same amount as before test2 308 -> removing the 60 widgets however, the 3 widgets built due to tabifying remains test3 368 -> build 60 widgets test4 368 test5 368 test6 371 -> 3 new widgets due to tabifying... test7 371
Is there a reason to get these 3 additional widgets and why the removeDockWidget does not delete it automatically ?
-
Hi,
You are building objects that are customWidget are you putting anything in there that is parented differently ?
Without seing the whole code it's difficult to determine what might be happening.
-
wrote on 31 Dec 2019, 06:23 last edited by
Hello SGaist, when I comment the "tabifyDockWidget" line, I do not have any issue.
Code inside the customWidgets is quite simple.
An example:
QWidget * widget = new QWidget(); setWidget(widget); QVBoxLayout * layout = new QVBoxLayout(); widget->setLayout(layout); m_map = new baseItemMap(); // With m_map beeing a QCustomPlot figure layout->addWidget(m_map);
Another example:
QScrollArea * scroll = new QScrollArea(); setWidget(scroll); scroll->setBackgroundRole(QPalette::Light); scroll->setFrameShape(QFrame::NoFrame); scroll->setWidgetResizable(true); QWidget * widget = new QWidget(); scroll->setWidget(widget); QVBoxLayout * layout = new QVBoxLayout(); widget->setLayout(layout); QLabel * labTX = new QLabel(tr("<b>Text</b>")); layout->addWidget(labTX); m_table = new customTableView(); // Derived from a QTableView layout->addWidget(m_table);
-
wrote on 31 Dec 2019, 06:57 last edited by
I've just tried with empty customWidget and I continue to "leak" widgets.
-
I just realized, when you call tabify, there's a QTabWidget that is created and tabs added for each widget you put together.
There you have the three widgets.
-
wrote on 31 Dec 2019, 14:18 last edited by
Thanks SGaist, that's an interesting point ! So, how could I remove these QTabWidgets ? Do I have to do it before or after the following piece of code ?
for(uncloseableDockWidget * w:m_widgets) { m_container->removeDockWidget(w); delete w; } m_widgets.clear();
-
wrote on 1 Jan 2020, 17:12 last edited by
I tried to remove the central widget like this :
delete m_container->centralWidget();
and build a new one. I thought that these additional widgets would be "parented" to the central widget. However, it does not work. Any idea to delete it ?
-
Well, I think you have found an edge case. From the quick look I did to the sources of QMainWindow and its internal classes, the fact that you are deleting the dock widget every time seems to not be handled in the "tabification" part (I have to take a deeper dive to ensure that).
I must say that it's pretty unusual to completely destroy dock widgets and recreate them like you are currently doing.
Would you mind providing a minimal compilable example to reproduce that ?
-
wrote on 2 Jan 2020, 10:58 last edited by
Thanks SGaist, here is a complete exemple :
main.cpp:
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <iostream> #include <QMainWindow> #include <QDockWidget> #include <QCloseEvent> #include <QLabel> #include <QPushButton> #include <QBoxLayout> #include <QApplication> class uncloseableDockWidget : public QDockWidget { Q_OBJECT public: /** * @brief Constructor * @param title: title * @param parent: parent widget * @param flags: flags */ explicit uncloseableDockWidget(const QString & title, QWidget * parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()) : QDockWidget(title, parent, flags) { setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); setAllowedAreas(Qt::AllDockWidgetAreas); } /** * @brief Destructor */ virtual ~uncloseableDockWidget() override {} public slots: /** * @brief Update view */ virtual void updateView() = 0; protected: /** * @brief Close event * @param event: event */ void closeEvent(QCloseEvent * event) override { event->ignore(); } }; class dock : public uncloseableDockWidget { Q_OBJECT public: dock(const QString & title, QWidget * parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()) : uncloseableDockWidget(title, parent, flags) {} void updateView() override {} }; class tab1 : public QWidget { Q_OBJECT public: tab1(QWidget * parent = nullptr) : QWidget(parent) { QVBoxLayout * layout = new QVBoxLayout(); setLayout(layout); QPushButton * bt = new QPushButton("test"); layout->addWidget(bt); connect(bt, SIGNAL(clicked()), this, SIGNAL(btClicked())); } signals: void btClicked(); }; class tab2 : public QWidget { Q_OBJECT public: tab2(QWidget * parent = nullptr) : QWidget(parent) { QVBoxLayout * layout = new QVBoxLayout(); setLayout(layout); m_content = new QMainWindow(); layout->addWidget(m_content); m_content->setCentralWidget(new QLabel("test")); } public slots: void update() { std::cout << "test1: " << QApplication::allWidgets().size() << std::endl; // Remove already existing dock widgets for(uncloseableDockWidget * w:m_docks) { m_content->removeDockWidget(w); delete w; } m_docks.clear(); std::cout << "test2: " << QApplication::allWidgets().size() << std::endl; // Add new dock widgets m_docks.emplace_back(new dock("title1")); m_docks.emplace_back(new dock("title2")); m_docks.emplace_back(new dock("title3")); std::cout << "test3: " << QApplication::allWidgets().size() << std::endl; // Add and tabify dock widgets for(uncloseableDockWidget * w:m_docks) m_content->addDockWidget(Qt::RightDockWidgetArea, w); std::cout << "test4: " << QApplication::allWidgets().size() << std::endl; for(unsigned int i=1; i<m_docks.size(); i++) m_content->tabifyDockWidget(m_docks[i-1], m_docks[i]); std::cout << "test5: " << QApplication::allWidgets().size() << std::endl; if(m_docks.size()) m_docks.front()->raise(); std::cout << "test6: " << QApplication::allWidgets().size() << std::endl; } private: QMainWindow * m_content = nullptr; std::vector<uncloseableDockWidget *> m_docks; }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget * /*parent*/ = nullptr) { m_tab = new QTabWidget(); setCentralWidget(m_tab); tab1 * t1 = new tab1(); m_tab->addTab(t1, "t1"); tab2 * t2 = new tab2(); m_tab->addTab(t2, "t2"); connect(t1, SIGNAL(btClicked()), t2, SLOT(update())); } ~MainWindow() { } private: QTabWidget * m_tab = nullptr; }; #endif // MAINWINDOW_H
Clicking various times on the "test" button in tab 1, you can see that number of widgets increases.
-
wrote on 10 Jan 2020, 19:29 last edited by
Hello SGaist, is the example I provided enough to understand my issue ? Do you think I could get a workaround in order to avoid "leaking" widgets ? Thnanks in advance.
-
So it looks like I never submitted my answer, sorry !
I took a look at the internals of QMainWindow and the handling of the QDockWidget "tabification" is more involved than what I thought.
There's no easy workaround per se but there's something I though about: do you really need to delete these dock widgets all the time ? Would a reset of their content be enough ? If you really need to delete them, what about just replacing the widget they contain ? Doing so, you would only have a set of QDockWidget and thus avoid the issue you are currently having.
In any case, I think it would be worth checking the bug report system to see if there's something related and if not, please open a repot providing your small code sample (including the corresponding pro file).
-
wrote on 12 Jan 2020, 20:06 last edited by
Thanks SGaist for you answers ! I can change the way I manage the dock widgets (I could just clear the QMainWindow which contains these docks even if I think it will not be as elegant as my first solution). I'll open a bug report as you propose.
-
I was rather thinking about cleaning the widgets within the dock widgets. So you keep the number of QDockWidgets constant.