I have a QWidget based class that I need to instantiate from another object.
If I create the widget in the body of its parent widget class it is all good:
new NodeWidget(rootItem, this); // this works
But when I use the aux method the widget is created, but never drawn.
rootItem->createWidget(this); // internally rootItem does the same thing as above
The method itself:
void createWidget(QWidget * target) {
if (!_ui) _ui = new NodeWidget(this, target);
...
}
The way I see it, both approaches do the same thing, but the first one works while the second one does not. The NodeWidget constructor runs, but the widget does not appear in the parent widget and the paintEvent is never called. Any ideas why?
EDIT: This is certainly odd, I noticed that the following line:
new QPushButton(this);
works when called in the constructor of the parent widget, but not when called from the mousePress event. What IS the difference?
Do you call show() for your custom widget?
If the child widget is created after it's parent is already showed the parent-child relationship can't show the child too, so you need to explicitly call show().
But when the widget is added in the constructor, the parent isn't showed yet and then when the parent is showed it automatically shows all the children that were not explicitly hidden.
Related
I am new with Qt and i am very confused about how widgets are deleted. I was reading a video and i wanted to show up a QProgressbar while the video frames are being read and then remove this QProgressbar when the video is loaded.
I have done it with 2 different ways:
Using Pointers
QWidget* wd = new QWidget();
QProgressBar* pB = new QProgressBar(wd);
QLabel* label = new QLabel(wd);
//setting geometry and updating the label and progressbar
wd->deleteLater();
wd->hide();
this code is written inside a class and i was assuming when the destructor of this class is called, the widget will be deleted with all of it's children but that didn't happen and everytime i run this function again a new widget is created without hiding or deleting the previous one (NOTE: i have tried to delete the label and progressbar from the widget assuming that they will disappear from inside the widget but this didn't happen "delete(pB);")
Using Objects
QWidget wd;
QProgressBar pB(&wd);
QLabel label(wd);
//setting geometry and updating the label and progressbar
wd.deleteLater();
wd.hide();
When i have run the same code but using objects instead of pointers , it has run exactly as i have wanted and everytime i run the function, the old widget is destroyed and a new one is created.
NOTE: -Also when i close the main window, in case of pointers, the widget wd still exists and the program doesn't terminate until i close them manually
- In case of Objects, when i close the main window everything is closed and the program is terminated correctly.
I need someone to explain me why is this happening and how if i am having a vector of pointers to widgets to delete all pointers inside that vector without any memory leakage
In typical C++ the rule would be "write one delete for every new". An even more advanced rule would be "probably don't write new or delete and bury that in the RIAA pattern instead". Qt changes the rule in this regard because it introduces its own memory management paradigm. It's based on parent/child relationships. QWidgets that are newed can be given a parentWidget(). When the parentWidget() is destroyed, all of its children will be destroyed. Hence, in Qt it is common practice to allocate objects on the stack with new, give them a parent, and never delete the memory yourself. The rules get more complicated with QLayout and such becomes sometimes Qt objects take ownership of widgets and sometimes they don't.
In your case, you probably don't need the deleteLater call. That posts a message to Qt's internal event loop. The message says, "Delete me when you get a chance!" If you want the class to manage wd just give it a parent of this. Then the whole parent/child tree will get deleted when your class is deleted.
It's all really simple. QObject-derived classes are just like any other C++ class, with one exception: if a QObject has children, it will delete the children in its destructor. Keep in mind that QWidget is-a QObject. If you have an instance allocated usingnew`, you must delete it, or ensure that something (a smart pointer!) does.
Of course, attempting to delete something you didn't dynamically allocate is an error, thus:
If you don't dynamically allocate a QObject, don't deleteLater or delete it.
If you don't dynamically allocate a QObject's children, make sure they are gone before the object gets destructed.
Also, don't hide widgets you're about to destruct. It's pointless.
To manage widget lifetime yourself, you should use smart pointers:
class MyClass {
QScopedPointer<QWidget> m_widget;
public:
MyClass() :
widget{new QWidget};
{
auto wd = m_widget->data();
auto pb = new QProgressBar{wd};
auto label = new QLabel{wd};
}
};
When you destroy MyClass, the scoped pointer's destructor will delete the widget instance, and its QObject::~QObject destructor will delete its children.
Of course, none of this is necessary: you should simply create the objects as direct members of the class:
class MyClass {
// The order of declaration has meaning! Parents must precede children.
QWidget m_widget;
QProgressBar m_bar{&m_widget};
QLabel m_label{&m_widget};
public:
MyClass() {}
};
Normally you'd be using a layout for the child widgets:
class MyClass {
QWidget m_widget;
QVBoxLayout m_layout{&m_widget};
QProgressBar m_bar;
QLabel m_label;
public:
MyClass() {
m_layout.addWidget(&m_bar);
m_layout.addWidget(&m_label);
}
};
When you add widgets to the layout, it reparents them to the widget the layout has been set on.
The compiler-generated destructor looks as below. You can't write such code, since the compiler-generated code will double-destroy the already destroyed objects, but let's pretend you could.
MyClass::~MyClass() {
m_label.~QLabel();
m_bar.~QProgressBar();
m_layout.~QVBoxLayout();
// At this point m_widget has no children and its `~QObject()` destructor
// won't perform any child deletions.
m_widget.~QWidget();
}
Actually im having 2 Qwidgets say "Widget" , "NewWidget". I'm calling "NewWidget" in "Widget" keypress event and viced versa,
void Widget::keyPressEvent(QKeyEvent *qv)
{
// i want to delete "Widget" here before i call "NewWidget"
NewWidget *newWidget = new NewWidget();
newWidget->setStyleSheet("background-color:black;");
newWidget->setGeometry(0,0,640,480);
newWidget->show();
}
I want to delete or destroy the "Widget" before calling "NewWidget"
Try the following
this->deleteLater();
It will attempt to destroy the widget when the function exits.
It sounds dangerous/bad to destroy the current widget when you are busy inside a member function of the widget that you want to destroy. Try the above code but otherwise think about redesigning your interactions.
Is there a order to declare widgets in Qt5(perhaps 4 too) ?
Consider the following pieces of code:
(just the a piece of the header to help me explain)
class ConfigDialog : public QDialog
{
Q_OBJECT
QGroupBox userAuthBox;
QGridLayout userAuthLayout;
QVBoxLayout dialogLayout;
QLabel userLabel;
QLabel passLabel;
QLineEdit userEdit;
QLineEdit passEdit;
};
this works as expected but just changing to (reordering declarations):
class ConfigDialog : public QDialog
{
Q_OBJECT
QLabel userLabel;
QLabel passLabel;
QLineEdit userEdit;
QLineEdit passEdit;
QGroupBox userAuthBox;
QGridLayout userAuthLayout;
QVBoxLayout dialogLayout;
};
this works also, but when the ConfigDialog goes out of scope happen a segfault.
I've saw this on other scenarios too, but always changing the order fix this.
My guess would be: you make your QGroupBox a parent of some of the other widgets.
Qt has a concept of parent-child relationship between QObjects. The parent is responsible for deleting its children when it itself is destroyed; it is assumed that those children were allocated on the heap with new.
Further, data members of a C++ class are constructed in the order they are listed in the class, and are destroyed in the reverse order.
Let's say userAuthBox is made a parent of userLabel (via setParent call, in your case executed by addWidget). In the first case, userLabel is destroyed first, and notifies its parent of this fact, whereupon userAuthBox removes it from its list of child widgets, and doesn't attempt to delete it.
In the second case, userAuthBox is destroyed first, and uses delete on its pointer to userLabel. But of course userLabel was not in fact allocated with new. The program then exhibits undefined behavior.
TL;DR: Yes! The order of declarations has a strictly defined meaning in C++. A random order will not work, as you've happened to notice.
You're not showing all the code. What is important is that one of the widgets is a child of the group box. Suppose you had:
class ConfigDialog : public QDialog
{
// WRONG
Q_OBJECT
QLabel userLabel;
QGroupBox userAuthBox;
QGridLayout userAuthLayout;
QVBoxLayout dialogLayout;
public:
ConfigDialog(QWidget * parent = 0) :
QDialog(parent),
dialogLayout(this),
userAuthLayout(&userAuthBox) {
// Here userLabel is parent-less.
Q_ASSERT(! userLabel.parent());
userAuthLayout.addWidget(&userLabel, 0, 0);
// But here userLabel is a child of userAuthBox
Q_ASSERT(userLabel.parent() == &userAuthBox);
}
};
The default destructor will invoke the destructors in the following order - it literally is as if you wrote the following valid C++ code in the destructor.
dialogLayout.~QVBoxLayout() - OK. At this point, the dialog is simply layout-less. All the child widgets remain.
userAuthLayout.~QGridLayout() - OK. At this point, the group box is simply layout-less. All the child widgets remain.
userAuthBox.~QGroupBox() - oops. Since userLabel is a child of this object, the nested userAuthox.~QObject call will execute the eqivalent of the following line:
delete &userLabel;
Since userLabel was never allocated using new, you get undefined behavior and, in your case, a crash.
Instead, you should:
Declare child widgets and QObjects after their parents.
Use C++11 value initialization if possible, or initializer lists in the constructor to indicate to the maintainer that there is a dependency between the children and the parents.
See this answer for details and a C++11 and C++98 solution that will force the mistakes to be caught by all popular modern static C++ code analyzers. Use them if you can.
Suppose I have a main Widget, which is used to view some objects. The names of those objects are stored in a QListWidget. Now when a user selects one object (one item of the QListWidget), I want to open another widget in a separate window which takes the name of the object as an argument.
class MainWidget
{
Q_OBJECT
public slots:
void openSelection();
};
class ChildWidget
{
public:
ChildWidget(QString name, QWidget* parent = nullptr);
};
void MainWidget::openSelection()
{
QString selectedObjectName = ui->objectsNamesList->selectedItem()->text();
ChildWidget* detaildedWiew = new ChildWidget(selectedObjectName, this);
detaildedWiew->show();
}
When I do this, the child widget opens, but it has no space of its own. It is locked in the area of the parent. I need to set the children free, to run around the screen freely, independent of their parent. How can I do this? Is there some Qt way, or do I have to define some "pseudo child" relationship and develop a system to properly delete the pseudo children?
You can use QWidget::setWindowFlags(Qt::Window) to make your widget a separate window.
Also have a look at Qt::WindowFlags.
Not sure I got the question right...
The default constructor of QWidget may accept two arguments:
QWidget *parent = 0 is the parent of the new widget. If it is 0 (the default), the new widget will be a window. If not, it will be a child of parent, and be constrained by parent's geometry (unless you specify Qt::Window as window flag).
Qt::WindowFlags f = 0 (where available) sets the window flags; the default is suitable for almost all widgets, but to get, for example, a window without a window system frame, you must use special flags.
So, if you pass anything but NULL to parent, your wiget will not be a separate window, unless you set the second parameter as QT::Window. This is what's happening for you. So you'll need to either set the flag QT::Window, or make your own class, derive it from QWidget, and write a constructor that takes an additional argument which will be the pointer you need, while getting NULL as parent.
I have inherited the QmainWindow class to use as the mainwindow for the application that I am building.
I have set the central widget as a pointer to another class, that I have created.
//main window constructor
postEntryWidget = 0; // null pointer to another class that extends QWidget
dataEntryWidget = new Data_Entry_Widget; //extends QWidget
setCentralWidget(dataEntryWidget); //set the widget in the main window
When the user clicks on an action, this sets the central widget to another pointer to another widget class.
/*
*this is the implementation of the slot that would be connected to the QAction
*connected to the postEntryWidget slot
*/
if(!postEntryWidget)
postEntryWidget = new Post_Entry_Widget;
setCentralWidget(postEntryWidget);
/*
*this is the implementation of the slot that would be connected to the QAction
*connected to the dataEntryWidget slot
*/
if(!dataEntryWidget)
dataEntryWidget = new Post_Entry_Widget;
setCentralWidget(dataEntryWidget);
This breaks when switching back and forth between views. And If I add a null point to the preceding view I lose the data when I go back to that view.
/*
*this is the implementation of the slot that would be connected to the QAction
*connected to the postEntryWidget slot
*/
dataEntryWidget = 0; //set the previous widget to a null pointer but loses data
if(!postEntryWidget)
postEntryWidget = new Post_Entry_Widget;
setCentralWidget(postEntryWidget);
How would I keep the state between the two views without creating a custom data structure or is this bad practice. I am most familiar with php and web dev so I am not sure if this is even the best way to go about this.
Thanks in advance
Not totally sure what your goal is. But if you are trying to permit someone the ability to go back to something they were working on, then perhaps you'd be better off using a tab widget instead of hiding the existence of that work?
QTabWidget documentation
Qt Tabbed Dialog example
So you'd make that your central widget, and plug the Post_Entry_Widget and Data_Entry_Widget instances under that. An advantage of that is that Qt manages the tab switching for you.
If you don't want tabs there is also a QStackedWidget, which just lets you programmatically switch between a set of widgets.
It is more complicated than it seems. The problem is, that when setCentralWidget() is called, current centralWidget() gets deleted. In order to preserve its contents, you need to remove it from the window by reparenting it to NULL or 0. Try to change your code to:
if(!postEntryWidget)
postEntryWidget = new Post_Entry_Widget;
if (centralWidget()) centralWidget()->setParent(0); //reparent if exists
setCentralWidget(postEntryWidget);
/*
...
*/
if(!dataEntryWidget)
dataEntryWidget = new Post_Entry_Widget;
if (centralWidget()) centralWidget()->setParent(0); //reparent if exists
setCentralWidget(dataEntryWidget);