Qt: How does parenting affect the layout of children widgets? - c++

I've been trying to learn Qt for the purposes of embedding a GUI on top of my OpenGL projects. In this case, the idea is to have my OpenGL viewport fill my main window. I have a simple QtWidget-based GUI that contains a 'RenderSurface' which is a subclass of QGLWidget. However, I've noticed that making my RenderSurface a child of my MainWindow has unwanted effects on my GUI's layout.
Here's the simple MainWindow code and screen shot without use of parenting:
#include "mainwindow.h"
#include "rendersurface.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
renderSurface()
{
setCentralWidget(&renderSurface);
setWindowTitle("QtGL Test");
}
MainWindow::~MainWindow()
{
}
In this case, my RenderSurface (QGLWidget subclass) is not passed a pointer to any parent QWidget when I call its constructor in the MainWindow initialization list. You can see that the dark grey OpenGL context fits the size of the window, and it seems to fill the window even when I expand and contract the window size.
Here's the same thing with parenting:
#include "mainwindow.h"
#include "rendersurface.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
renderSurface(this)
{
setCentralWidget(&renderSurface);
setWindowTitle("QtGL Test");
}
MainWindow::~MainWindow()
{
}
Simply passing 'this' to the RenderSurface constructor instead changes the way that my OpenGL context is initially rendered in my window. It's also worth noting that my RenderSurface will start filling my window properly once I resize the window by dragging an edge.
Why does making my RenderSurface a child of my MainWindow cause this issue? How can I avoid this buggy-looking behaviour? Also, since my GUI seems to work better without parenting, what are some pros and cons of object parenting in Qt?

Case 1 : You are effectively using QMainWindow layout, which is a specific layout implementation to the same extent as QGridLayout or QStackedLayout.
Case 2 : You are using the default Qt widget layout, which have a more loose setup of the children widgets. The only condition I know of is that children rectangles are within the parent rectangle and their default position is 0,0. The layout will not resize the children.
setCentralWidget(&renderSurface);will try to add renderSurface to the Qmainwindow layout. In case 2, rendersurface is already inside a layout, so this attempt will fail.
Also, since my GUI seems to work better without parenting
You are incorrect about the parenting part. In both case renderSurface becomes a child of the window.
QWidget* w = new Qwidget(0);
QLayout* l = new QHBoxLayout();
l->add(w);
setLayout(l);
During setLayout all widgets in the layout will have their affinity changed to the widget owning the layout. w is no more a parentless widget.
When a widget is going to be added to a layout, the best thing is to create it without a parent like you did. The correct parent is going to be set later.

It is a known issue. I like this answer as well: A widget with a parent doesn't become a window but is embedded into its parent. If you don't put it in a layout it might end up in a strange place on the parent and might have an arbitrary size (0x0 included). So if you want your widget to be a window, simply don't pass a parent at all. So the layout you put the widget in is the key for rendering in this case and QMainWindow is not a layout but can have a layout or a few on its own.
On the other hand, setCentralWidget is already taking care of releasing the embedded widget so there is no reason to provide the parent pointer for such widget. And setCentralWidget makes an unparented widget central and correctly drawable as if in layout.

Related

Qt Widgets, is it mandatory to set the containing widget as parent?

I would like to insert a QWidget inside a container QWidget (via its layout), but avoid parenting the inserted widget to the container widget.
There are good reasons behind this, the inserted widget is a toolbox tied to a document, and this toolbox is sent to a floating dock widget when the document becomes the active document.
Is this possible?
I would like to insert a QWidget inside a container QWidget (via its
layout), but avoid parenting the inserted widget to the container
widget.
There are good reasons behind this, the inserted widget is a toolbox
tied to a document, and this toolbox is sent to a floating dock widget
when the document becomes the active document.
Is this possible?
This simple answer is No - cannot avoid the widget being parented. But hold on. First, why the answer is No. Then how can we still do what you want.
Setting the layout for the widget does an implicit parent set.
Setting the widget for the layout does an implicit parent set.
QVBoxLayout* layout = new QVBoxLayout; // no parent yet
this->setLayout(layout); // it does layout->setParent(this);
QWidget* widget = new MyWidget; // no parent yet
layout->addWidget( widget ); // it does widget->setParent(this);
If layout is the layout manager on a different widget, setLayout()
will reparent the layout and make it the layout manager for this
widget.
With QLayout the reparenting is a bit more complicated because addWidget does call addItem:
void QLayout::addItem(QLayoutItem * item)
Implemented in subclasses to add an item. How it is added is specific
to each subclass.
This function is not usually called in application code. To add a
widget to a layout, use the addWidget() function; to add a child
layout, use the addLayout() function provided by the relevant QLayout
subclass.
Note: The ownership of item is transferred to the layout, and it's the
layout's responsibility to delete it.
See also addWidget(), QBoxLayout::addLayout(), and
QGridLayout::addLayout().
void QLayout::addWidget(QWidget * w)
Adds widget w to this layout in a manner specific to the layout. This
function uses addItem().
But we can still do something about such request:
There are good reasons behind this, the inserted widget is a toolbox
tied to a document, and this toolbox is sent to a floating dock widget
when the document becomes the active document.
Is this possible?
But that is of course possible. Say, by calling QWidget::setParent which is quite a common practice when we need to move the parent into new layout.

Qt QWidget::setGeomerty

i'm stuck with a simple Function of Qt that does not work for me.i made a class that inherits
from QMainWindow and another class that inherits from QWidget.then i made from the second a member object(a pointer to) inside the first and assigned it as its centralWidget during the construction of my window.
when it comes to adjust my centraWidget inside the window with the function QWidget::setGeomerty() it simply don't work.here's my code:
void MainWindow::show()
{
//some code that centers my window on the screen
int margin=this->width()/7;
centralWidget()->setGeometry(margin,centralWidget()->geometry().top(),this->width()-margin,centralWidget()->geometry().bottom());
QMainWindow::show();
}
i know it might be stupid but i just can't figure it out.help me.
QMainWindow has its own layout in which the center area is occupied by the central widget. So it won't be pretty straightforward to break that layout and modify the central widget size / position arbitrarily.
What I recommend is to use a placeholder central widget and add your widget as a child.
I'm pretty sure you can achieve what you want by setting a proper Qt built in layout to the "placeholder" central widget and then adding your widget to the layout.
layouts was necessary to manage what i want; i think it's not possible to hand directly over the central widget and try to move/resize it;but by adding a layout and a child widget, we can dispose of them.here is my code:
mainwindow.cpp
// i focused on my window constructor
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_MainView(new MainView(this)),
m_WindowLayout(new MainWindLayout(NULL))//my custom layout witch just inherits from QGridLayout
{
//.............
setCentralWidget(m_MainView);
m_WindowLayout=new MainWindLayout(m_MainView);//my layout set into my central Widget"m_MainView".
QWidget *temp(dynamic_cast<QWidget *>(m_MainView->centralView()));//centralView() returns a child QWidget of my central Widget
QMargins margins(this->width()/5,0,this->width()/5,0);//setting up layout margins :left,top,right,bottom;exactly what i need
m_WindowLayout->setContentsMargins( margins);
m_WindowLayout->addWidget(temp,0,0,-1,-1);//adding my child widget to the layout filling all cells of the gridlayout.
}
thanks everybody

QMainWindow centralWidget border

I have a QMainWindow whose central widget has been set to a QGraphicsView viewing a black scene (for test purposes). Note that in the code below, I use my class derived from QGraphicsView, called CQtGlView, which reimplements only the resizeEvent function.
Regardless of whether I add the view directly,
CQtMainWindow::CQtMainWindow() {
m_glView = new CQtGlView();
setCentralWidget(m_glView);
}
or stick it in a layout with margins of 0 in a dummy widget,
CQtMainWindow::CQtMainWindow() {
m_glView = new CQtGlView();
QWidget* dummy = new QWidget();
QHBoxLayout* l = new QHBoxLayout();
l->setContentsMargins(0,0,0,0);
l->addWidget(m_glView);
dummy->setLayout(l);
setCentralWidget(dummy);
}
I get an unwanted grey border around the widget.
The screenshot below illustrates the problem, visible between my scene and the windows aero border.
This would not be a problem if my application did not allow switching to full screen. The border is very obvious once the rest of the screen is black.
It's possible this area represents the DockWidgetAreas around the outside of the central widget.
Is there anything I can do to solve this other than not use QMainWindow? (Undesirable due to my use of menuBar, tool bars, and statusBar.)
It turns out that QGraphicsView derives from QFrame, where I assumed it was only a QWidget.
The solution to this problem was to call setFrameStyle(QFrame::NoFrame); in the constructor of my QGraphicsView subclass. Or if it was not a subclass,
m_glView->setFrameStyle(QFrame::NoFrame);
Have you tried setFrameShape(QFrame::NoFrame) on the QGraphicsView?

Setting a QDialog to be an alien

In a standalone GUI application where I don't have a windowmanager nor a composite manager I want to display a QDialog to the user to ask for values.
The dialog is quite big, so I want to make it translucent so that the user can look through it to see what happens in the application while the dialog is shown.
The problem is that for translucency of X native windows, there needs to be a composite manager. Qt internal widgets can be painted translucent because they don't correspond to native X windows (aliens) and are completely only known to Qt.
Is there a way to make the background of a QDialog translucent without having a composite manager running? Perhaps making it a normal child widget/alien of the application's main window? Is there a better alternative to this?
I don't know of any way of turning a QDialog into a normal child widget. Looking at the Qt for X11 code, I can't figure out a way of not setting the Qt::WindowFlags passed to the QWidget (parent) constructor so that it would be a plain widget and not a window of its own (but I could be wrong, didn't spend a lot of time on that).
A simple alternative is to use a plain QWidget as your container, instead of a QDialog. Here's a sample "PopupWidget" that paints a half-transparent-red background.
#include <QtGui>
class PopupWidget: public QWidget
{
Q_OBJECT
public:
PopupWidget(QWidget *parent): QWidget(parent)
{
QVBoxLayout *vl = new QVBoxLayout;
QPushButton *pb = new QPushButton("on top!", this);
vl->addWidget(pb);
connect(pb, SIGNAL(clicked()), this, SLOT(hide()));
}
public slots:
void popup() {
setGeometry(0, 0, parentWidget()->width(), parentWidget()->height());
raise();
show();
}
protected:
void paintEvent(QPaintEvent *)
{
QPainter p(this);
QBrush b(QColor(255,0,0,128));
p.fillRect(0, 0, width(), height(), b);
}
};
To show it, call it's popup() slot which will raise it to the top of the widget stack, make it as large as its parent, and show it. This will mask all the widgets behind it (you can't interact with them with the mouse). It hides itself when you click on that button.
Caveats:
this doesn't prevent the user from using Tab to reach the widgets underneath. This could be fixed by toggling the enabled property on your "normal" widget container for example. (But don't disable the PopupWidget's parent: that would disable the popup widget itself.)
this doesn't allow for a blocking call like QDialog::exec
the widgets in that popup won't be transparent, you'd have to create custom transparent-background versions for all the widget types you need AFAIK.
But that's probably less of a hassel than integarting a compositing manager in your environment.

GtkVBox Qt equivalent

In GTK, I used to have a window to which I gtk_container_add()'d a GtkVBox. Then I could pack widgets I wanted to the GtkVBox to have them appear in the window.
Now I've decided to try out Qt, but I can't seem to figure out how to do this in Qt.
Right now what I've done is create a QMainWindow, but I found that you can only pack one main widget into it, which is obviously quite limiting. So I wanted to create something like the GtkVBox and use that as the main widget and then add other widgets to this box.
What I've found by Googling are however only the Q3VBox widget, which seems to be what I want, but is deprecated, and the QVBoxLayout.
I tried to use the QVBoxLayout, but it tells me that I cannot change the layout of my QMainWindow since it already has a layout.
Edit: Here is how I do it (this is in the constructor):
box = new QVBoxLayout;
setLayout(box)
It compiles fine, but during runtime, it prints on the console:
QWidget::setLayout: Attempting to set QLayout "" on HCGWindow "", which already has a layout
(HCGWindow is my app's window, which is a subclass of QMainWindow)
So, how can I create something similar to a GtkVBox in Qt, and if the solution is the Q3VBox, why is it deprecated and what other thing should I use?
Thanks
In fact, here is the recommended solution provided by the Qt documentation.
You should create a QVBoxLayout and add the widgets you want in it. After that, you set the layout on another empty widget and then you set this widget as the central widget of the QMainWindow subclass. Here is an example in code:
QWidget* widget1 = new QWidget(); // This could be anything subclassing QWidget.
QWidget* widget2 = new QWidget();
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(widget1);
layout->addWidget(widget2);
QWidget* central = new QWidget(); // Only a containing QWidget.
central->setLayout(layout);
this->setCentralWidget(central);
Now, you QMainWindow subclass should have the two QWidgets in it, arranged in a QVBoxLayout.
Note here that I did not give any parent to anyone. You could have done it, but when you call addWidget or setCentralWidget, the ownership of the widget (and the layout) is given to the containing class.
If you read a bit about Qt, you'll know that this allows the parent to destroy his children when he is about to be destroyed itself.
Finally, note that QMainWindow is an exception and, from what I know, is the only class with setCentralWidget as a method. If you attempt to create a QWidget subclass, you will be able to use setLayout (as shown in the example above).
Hope this helps.
Try to create QVBoxLayout instance, add your widgets to it and add (not replace) this layout to main window's layout.
Note, QLayout class has no member addLayout, but subclasses has one.
Firstly you must get and remember classname of main window's layout:
qDebug(this.layout()->objectName);
Then add your QVBoxLayout to window's layout:
dynamic_cast<YourWindowLayoutClass>(
this.layout())->addLayout(your_qvboxlayout_object);
I hope it will work.