Make a QHBoxLayout accesible in an Instance of a QWidget - c++

I want to Subclass a QWidget...
Widget::Widget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ImgWidget)
{
// Is this the right place or does it need to be outside like: Widget::layout...
QHBoxLayout *hLayout = new QHBoxLayout;
...
hLayout->addWidget( someWidget );
this->setLayout(hLayout);
}
...and then in an instance i would like to add a widget to the layout like
Widget *widget = new Widget();
...
widget->hLayout->addWidget( someOtherWidget );
The equivalent in Python would be done by the 'self' keyword like self.layout = QHBoxLayout() but i think it cant be done with the this keyword
But more general... What is the best way to do this in C++ because i used it a lot with pyqt

Any QWidget can provide access to the layout via the layout function.
Widget* widget = new Widget();
QLayout* layout = widget->layout();
I don't know why you want to externally modify the layout of the widget but at this point you need knowledge of that actual layout type. With knowledge of the layout type you can cast and use it.
QHBoxLayout* hLayout = qobject_cast<QHBoxLayout>(layout);

Implement a function addSubWidget ( QWidget* aubWidget ), the rest of the code is ok, and make hLayout private. Or batter, use the layout function with a cast, like #James sugested.
In c++ it is just recomended, not mandatory to use this->hLayout to be sure you do notshadow it with a local variable with the same name.

Related

Use this outside of constructor

MainWindow::MainWindow(QWidget *parent, GraphicalUI *graphicalUI) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QLabel *label = new QLabel("Label", this);
label->setPixmap(graphicalUI->textures["background"]);
label->setStyleSheet("background-color: black;");
}
void buildWindow(Level *level, GraphicalUI *graphicUI) {
QGridLayout layout = QGridLayout(this);
}
The problem here is found in the buildWindow() function. Obviously I cannot just use
QGridLayout(this). But I want the MainWindow to be the parent of my QGridLayout.
How do I do this? Also, the buildWindow function is going to be called externally.
this is an implicit argument to all member functions. Which means it is available in all member functions not just the constructor. It's just a pointer to the current object.
Since the buildWindow function is not a member function of MainWindow(which I do not recommend) you can pass the address of the MainWindow instance to buildWindow function. By default MainWindow is instantiated in the main.cpp file when you create a new GUI project in Qt Creator.
like:
void buildWindow(Level *level, GraphicalUI *graphicUI, MainWindow* window) {
QGridLayout* layout = new QGridLayout(window);
}
But:
In your code:
void buildWindow(Level *level, GraphicalUI *graphicUI) {
QGridLayout layout = QGridLayout(this);
}
variable layout is a local variable in the scope of this function. When your code reaches the end of function (closing brackets), your QGridLayout layout object is destroyed. In order to avoid this, you should use pointers and the new keyword.
QGridLayout *layout = new QGridLayout(this);
now leyout is just a pointer to a QGridLayout object. This object is destroyed if you call delete layout; manually or the parent object (in this case your mainwindow object) get's destroyed.
But now the problem is you can't access this QGridLayout later on from other functions because the pointer layout will be lost at the end of this function. I recommend using a class member variable like:
private:
QGridLayout *layout;
in your header file and initialize if inside your function like:
void MainWindow::buildWindow(Level *level, GraphicalUI *graphicUI) {
layout = new QGridLayout(this);
}
Make sure you don't use the layout pointer before calling this function.
void MainWindow::buildWindow(Level *level, GraphicalUI *graphicUI) {
QGridLayout* layout = new QGridLayout(this);
}
Thats the solution.
The function is also required to exist in the header of the class.

Change background color of a Qlayout containing QButtons

I have this Qt code:
QHBoxLayout *taggerBox = new QHBoxLayout;
falseBtn = new QToolButton;
falseBtn->setText(tr("False"));
voidBtn = new QToolButton;
voidBtn->setText(tr("Void"));
taggerBox->addWidget(falseBtn);
taggerBox->addWidget(voidBtn);
I would like to change the background of the QHBoxLayout (NOT the background of each button). I didn't find any way to change the background color of a QLayout.
How can I do this ?
Thanks!
QLayout is not a visual element, it's a container that adjust location of contained widgets. You can change background of QFrame or other widgets you included QLayout into.
Since QHBoxLayout is not a QWidget it hasn't its own appearance. So you can't change its color.
You'll have to add an intervening widget that you set the layout on, and change that widget's background. E.g.
auto *taggerBox = new QWidget;
auto *layout = new QHBoxLayout(taggerbox);
falseBtn = new QToolButton(tr("False"));
voidBtn = new QToolButton(tr("Void"));
layout->addWidget(falseBtn);
layout->addWidget(voidBtn);
auto palette = taggerBox->palette();
palette.setColor(QPalette::Window, Qt::blue);
taggerBox->setPalette(palette);
If you're doing this in the constructor of some class, then likely the objects have the same lifetime as the class as there's no point to dynamically allocate them. In such circumstances, the widgets and layouts should be class members instead:
class MyClass : ... {
QWidget taggerBox;
QHBoxLayout taggerLayout{&taggerBox};
QToolButton falseBtn{tr("False")};
QToolButton voidBtn{tr("Void")};
public:
MyClass(...);
};
MyClass::MyClass(...) : ... {
taggerLayout.addWidget(&falseBtn);
taggerLayout.addWidget(&voidBtn);
auto palette = taggerBox.palette();
palette.setColor(QPalette::Window, Qt::blue);
taggerBox.setPalette(palette);
...
}

adding a Q3DScatter to an existing QDialog form

I would like to insert a Q3DScatter instance onto one of my forms. I am able to construct the graph and display it with:
Q3DScatter *graph = new Q3DScatter();
QWidget *container = QWidget::createWindowContainer(graph);
...do some graph stuff...
container.show()
This successfully creates the graph and shows it in its own window/container. I would now like to place it inside another form that has been designed in qt-creator. I have placed an empty widget on the form(QDialog) as a placeholder, but am unable to move the Q3DScatter into the widget.
I see Q3DScatter::Q3DScatter(const QSurfaceFormat *format = Q_NULLPTR, QWindow *parent = Q_NULLPTR) in the documentation but I dont have a QWindow parent to attach it to, as the form I would like to add it to is a QDialog...
I am very new to widgets in general, so please correct me if this is the wrong approach.
Add the container as a child of the dialog. Put in a layout if you need to resize when the dialog is resized. Example:
MyDialog::MyDialog(Q3DScatter *scatter, QWidget *parent) : QDialog(parent)
{
QHBoxLayout * const l = new QHBoxLayout(this);
l->setMargin(0);
l->addWidget(QWidget::createWindowContainer(scatter, this);
}

qobject_cast<QVBoxLayout*>(layout()), is the appropriate cast?

Considering the layout was set in a QWidget with the following code:
setLayout(new QVBoxLayout);
And then it needs to be retrieved (to add more stuff to the layout). This was done with the following code:
QHBoxLayout *hLayoutTime(new QHBoxLayout);
qobject_cast<QVBoxLayout*>(layout())->addLayout(hLayoutTime);
qobject_cast is the appropriate kind of cast to use here?
To avoid unneded casting write this like this:
void YourWidget::setupContents()
{
QVBoxLayout *vLayout = new QVBoxLayout(this); // effectively this does setLayout(new QVBoxLayout);
QHBoxLayout *hLayoutTime(new QHBoxLayout);
vLayout->addLayout(hLayoutTime);
… … …
}
Looking the code in your current example, Why don't you just get the pointer when creating?
auto *vLayout = new QVBoxLayout;
auto *hLayoutTime = new QHBoxLayout;
vLayout->addlaout(hLayoutTime);
Answering your question, probably the most adequate cast is:
dynamic_cast<QHBoxLayout*>(new QVBoxLayout);
dynamic_cast has several checks and benefits over static_cast, so it is better to use it when possible.

Getting Parent Layout in Qt

quick question. Is there any way to (easily) retrieve the parent layout of a widget in Qt?
PS: QObject::parent() won't work, for logical reasons.
EDIT:
I'm positive the widget has a parent layout, because I added it to a layout earlier in the code. Now, I have many other layouts in the window and while it is possible for me to keep track of them, I just want to know if there is an easy and clean way to get the parent layout.
EDIT2:
Sorry, "easy and clean" was probably not the best way of putting. I meant using the Qt API.
EDIT3:
I'm adding the widget to the layout like this:
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(button);
SOLVED!
Usage: QLayout* parentLayout = findParentLayout(addedWidget)
QLayout* findParentLayout(QWidget* w, QLayout* topLevelLayout)
{
for (QObject* qo: topLevelLayout->children())
{
QLayout* layout = qobject_cast<QLayout*>(qo);
if (layout != nullptr)
{
if (layout->indexOf(w) > -1)
return layout;
else if (!layout->children().isEmpty())
{
layout = findParentLayout(w, layout);
if (layout != nullptr)
return layout;
}
}
}
return nullptr;
}
QLayout* findParentLayout(QWidget* w)
{
if (w->parentWidget() != nullptr)
if (w->parentWidget()->layout() != nullptr)
return findParentLayout(w, w->parentWidget()->layout());
return nullptr;
}
(Updated answer)
I guess it is not easily possible then. Since a Widget can be technically contained in multiple layouts (a horizontal layout which is aligned inside a vertical layout, for instance).
Just remember that a QWidget's parent does not change if it is aligned in a layout.
You possibly have to keep track of that yourself, then.
Simply use:
QHBoxLayout* parentLayout = button->parentWidget()->layout();
I assume button is a child of the widget which contains the layout which contains button. button->parentWidget() returns a pointer to the widget of the button's parent and ->layout() returns the pointer to the layout of the parent.
After some exploration, I found a "partial" solution to the problem.
If you are creating the layout and managing a widget with it, it is possible to retrieve this layout later in the code by using Qt's dynamic properties. Now, to use QWidget::setProperty(), the object you are going to store needs to be a registered meta type. A pointer to QHBoxLayout is not a registered meta type, but there are two workarounds. The simplest workaround is to register the object by adding this anywhere in your code:
Q_DECLARE_METATYPE(QHBoxLayout*)
The second workaround is to wrap the object:
struct Layout {
QHBoxLayout* layout;
};
Q_DECLARE_METATYPE(Layout)
Once the object is a registered meta type, you can save it this way:
QHBoxLayout* layout = new QHBoxLayout;
QWidget* widget = new QWidget;
widget->setProperty("managingLayout", QVariant::fromValue(layout));
layout->addWidget(widget);
Or this way if you used the second workaround:
QHBoxLayout* layout = new QHBoxLayout;
QWidget* widget = new QWidget;
Layout l;
l.layout = layout;
widget->setProperty("managingLayout", QVariant::fromValue(l));
layout->addWidget(widget);
Later when you need to retrieve the layout, you can retrieve it this way:
QHBoxLayout* layout = widget->property("managingLayout").value<QHBoxLayout*>();
Or like this:
Layout l = widget->property("managingLayout").value<Layout>();
QHBoxLayout* layout = l.layout;
This approach is applicable only when you created the layout. If you did not create the layout and set it, then there is not a simple way of retrieving it later. Also you will have to keep track of the layout and update the managingLayout property when necessary.
use widget.parent().layout() and search brute force (recursion included) is my only advice. Maybe you can search be "name".
Have you tried this? Don't forget to check for NULL.
QLayout *parent_layout = qobject_cast< QLayout* >( parent() );
If parent_layout equals NULL, then the parent widget is not a layout.
Have you tried QWidget::layout() ?