Adding custom QWidget during runtime - c++

I'm trying to implement a custom widget hierarchy:
QMainWindow -> QFrame -> MyWidget -> QFrame -> MySubWidget
Here is how MyWidget class looks like:
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget *parent = 0, ...);
...
public slots:
void SlotFunction(int i);
...
private:
MySubWidget *sub_w;
QFrame *sub_frame;
...
}
If I try to create an MySubWidget during MyWidget constructor, then all MySubWidget elements are shown as intended:
MyWidget::MyWidget (...) : QWidget(parent) {
...
sub_frame = new QFrame(this);
...
sub_w = new MySubWidget(sub_frame); // commented out on a runtime test
}
But if I try to add subwidget during runtime, sub_frame remains blank. I.e. signal reaction:
void MyWidget::SlotFunction(int i) {
sub_w = new MySubWidget(sub_frame); // update, repaint, show and hide methods aren't helphul
}

I know this is an old question, but I was having a very similar issue and it turned out to be a lack of call to the QWidget::show(). Perhaps that was your problem as well?
My question here: Dynamically add instance inherited from QWidget
Cheers.

Are you reaching your function?
At the top of your function before making a new instance of MySubWidget put:
qDebug() << Q_FUNC_INFO;
Is the slot connected properly?
Qt will let you know if it is unable to connect a slot using a runtime warning. Look at the debug output that shows up in Qt Creator and it may mention a reason why the slot was never reached.
Is subframe visible?
If the parent of your object isn't visible, then showing or hiding the child object will only affect it when the parent is shown.
Hope that helps. Good luck.

Related

How to open a second QMainWindow from my first/original MainWindow?

I am creating a desktop app with Qt6 and C++ and I have my original MainWindow class. Using Qt Creator I generated ui,h,cpp for a new SummaryClass (QMainWindow).
I want to be able to click a button located in MainWindow so that I can open the SummaryWindow.
void MainWindow::openSummary()
{
SummaryWindow window;
window.show();
}
I understand that at the end of the function the window instance falls out of scope and gets destroyed. (the destructor generated by Qt Creator gets automatically called) since the window appears then disappears quickly.
If I were to simply execute
SummaryWindow window = new SummaryWindow();
window.show();
The window would show successfully but then that creates a memory leak.
Are there workarounds/solutions for what I want to achieve?
To be clear, I want to open the window and keep both windows visible.
one alternative is that you define a list of pointers to the summaryClass and you create and show as many instances of the summary as you need in the slot for the button in the mainWindow
your mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QList<SummaryClass*> l;
};
and the slot of the button
void MainWindow::on_pushButton_clicked()
{
SummaryClass* sm = new SummaryClass(this);
l.push_back(sm);
sm->show();
}
as soon as you do this:
new SummaryClass(this);
every summary class will be destroyed when the mainWin is destroyed....
In the constructor of the SummaryWindow write:
SummaryWindow::SummaryWindow() {
this->setAttribute(::Qt::WA_DeleteOnClose);
...
}
That makes the trick. Now
auto window = new SummaryWindow();
window->show();
does not cause a memory leak. (To ensure, you can add some debug printing into ~SummaryWindows()).

QDialog derived class

I have derived class from QDialog that I use for showing the charts. The constructor looks like this:
myplot::myplot(QDialog *parent) : QDialog(parent)
{
chartView = new QChartView();
chart = new QChart();
Qt::WindowFlags flags = 0;
flags |= Qt::WindowMaximizeButtonHint;
flags |= Qt::WindowCloseButtonHint;
setWindowFlags(flags);
this->setLayout(new QVBoxLayout);
this->layout()->addWidget(chartView);
this->setModal(1);
chartView->setChart(chart);
}
I call my class from Mainwindow.cpp but dialog does not close after App exit:
myplot* plot = new myplot(); //does not close after app exit
plot->do_something();
plot->show();
I thought I will correct the problem by this but it does not work:
myplot* plot = new myplot(this); //does not work
When I use this the dialog is closed immediatelly
myplot plot; //immediatelly close
plot.do_something();
plot.show();
When I use exec instead of .show() I get the error "Debug Assertion Failed, _CtrlIsValidHeapPointer(block)" after dialog is closed
plot.exec();
//work but after exiting dialog error
Please, how to handle correctly my derived class to be closed after App exit? I also want to have myplot class to be not modal (now I have it modal in order the user would close it manually before app exit).
Added header file:
#ifndef MYPLOT_H
#define MYPLOT_H
class myplot : public QDialog
{
Q_OBJECT
private:
public:
explicit myplot(QDialog *parent = nullptr);
signals:
};
#endif // MYPLOT_H
This is probably a duplicate of this question.
The answer is you have to write that functionality yourself. If you have your myplot object as a member variable of your MainWindow class then you can properly close it in the MainWindow::closeEvent. See the linked answer above for a hint on how you might implement this.
BTW: Then you don't have to make your dialog modal to enforce closing it before the MainWindow.
you need to look what the QDialog looks like...
do: pass a QWidget instead of a QDialog to the constructor, add a destructor to the dialog and delete there all the things created by the myplot instance if any, call exec() instead of show()...
class myplot : public QDialog
{
Q_OBJECT
public:
explicit myplot(QWidget *parent = nullptr);
~myplot();
....

Adding widget to layout only works in constructor of custom class

I have a class MainWindow subclassing QMainWindow. As the central widget I have a QScrollArea with a QWidget inside, as a container for my own custom widgets. I set a QVBoxLayout as the layout for the QWidget container and pass this layout to the constructor of my custom class, which is supposed to dynamically create instances of my custom widget class and add them to the layout.
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = Q_NULLPTR);
virtual ~MainWindow ();
private:
QVBoxLayout mainLayout;
QScrollArea scrollArea;
QWidget container;
NotificationControl control;
};
MainWindow::MainWindow (QWidget *parent) :
QMainWindow(parent),
container(&scrollArea),
control(&mainLayout) {
container.setLayout(&mainLayout);
scrollArea.setWidget(&container);
setCentralWidget(&scrollArea);
}
Now in my NotificationControl class I have a method addNotification:
void NotificationControl::addNotification (Notification notif) {
qDebug() << "NotificationControl addNotification" << notif.get_app_name();
NotificationWidget* widget = new NotificationWidget(notif);
container->addWidget(widget);
}
When this method is called, I get the debug output, but nothing is added to the layout. But if I add this to the end of the constructor of NotificationControl...
NotificationWidget* notif = new NotificationWidget(Notification());
container->addWidget(notif);
...for some reason it works. I'm pretty sure the problem isn't the NotificationWidget class. For testing purposes I made some changes, it doesn't actually use the Notification object that is passed at all.
What could be the problem?
Edit: I just noticed that adding widgets later (not in constructor) makes the widgets added in the constructor disappear.
Any widget added as a child widget must be explicitly shown if the parent widget is already visible. Usually, the child widgets are added before the parent widget is shown, and they then become visible themselves, too. But if you add them later, you must make them explicitly visible.

QDockWidgets merging incorrectly

I have a QDockWidget class and a QMainWindow:
// docker.hpp
class Docker : public QDockWidget
{
Q_OBJECT
public:
Docker(QString title, QWidget* parent = 0);
}
// docker.cpp
Docker::Docker(QString title, QWidget* parent): QDockWidget(title, parent)
{
QWidget* widget = new QWidget(this);
widget.setMinimumSize(200, 200);
setWidget(widget);
widget->setStyleSheet("border:5px solid gray;");
setAllowedAreas(Qt::AllDockWidgetAreas);
}
// mainwindow.hpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent);
private slots:
void createDockers();
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
{
setDockOptions(dockOptions() |
QMainWindow::AllowTabbedDocks |
QMainWindow::GroupedDragging);
// The following line of code does not change the situation.
// setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
// There are some other codes which connect a button to the void createDockers() method
}
void createDockers()
{
Docker* dock = new Docker("Docker", this);
dock->setFloating(true);
dock->show();
}
I am able to create two Dockers with clicks of the button mentioned above.
However, when I drag one QDockWidget onto the other, the border disappears and no tabs show up:
I am expecting the following to happen: (Achieved by spawning several QDockWidgets)
I am also noticing that one of the QDockWidgets did not vanish. Instead, it merged back to the MainWindow. This only happens if they are the "first two" QDockWidgets.
What caused this problem and how to solve it? I am trying to mimic this project.
I guess it's linked to the QMainWindow::GroupedDragging option. I'm pretty sure it should work well without it (I mean for the not showing tab issue). Do you have restrictions on dock position somewhere else? The documentation implies it could create issues: http://doc.qt.io/qt-5/qmainwindow.html#DockOption-enum
For the style issue, you may need to redefine it on tab event, because once tabbed, the widget may inherit the tab style instead of the dock widget style you defined (not certified at all ^^)
Last guess/thing you can try, is to start with the dock tabbed and not floating to see if you have any new bahaviour, it was what I was doing in a previous project and it was working pretty well.
Sorry but no other ideas for the moment.

Qt: why does connect() work only in the main window class?

Here's a simple code that creates a button and assigns a onclick handler:
auto btn = new QPushButton("CLICK ME");
connect(btn, SIGNAL(clicked()), this, SLOT(btn_Click()));
private slots:
void btn_Click() {
alert("clicked!");
}
It works as it should if called in the main window class. However when I try to do this in a child window, clicking the button does nothing. The child window is shown like this:
auto settingsWindow = new SettingsWindow();
settingsWindow->show();
I guess it's somehow connected with the receiver object which is now a different window. But how can I make it work?
In order to be able to declare signals/slots in your own class you should include Q_OBJECT directive in your class:
class SettingsWindow {
Q_OBJECT
...
};
You should add a MACRO in class SettingsWindow to enable singal receiving.
Add "Q_OBJECT" like the following.
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget();
....