QT5.4 remove and delete widget in a layout - qt

I have a QStackedLayout which contains few QWidget on it. The widgets layout is QVBoxLayout which have also few QPushButton. What I wanted to do is to remove all the widgets inside the QStackedLayout and then delete the widgets and layouts BUT I want all the buttons not to be deleted 'cause I will put them to another widget later.
Here's my code:
while (QLayoutItem *item = m_stacked_layout->takeAt(0)) {
QWidget *w = item->widget();
for (int i = 0; i < w->layout()->count(); i++) {
QPushButton *button = qobject_cast<QPushButton *>(w->layout()->itemAt(i)->widget());
if (button) {
w->layout()->removeWidget(button);
}
}
delete w;
delete item;
}
The application crashes at the line delete w;. And, if I remove the delete lines, application works fine.

BUT I want all the buttons not to be deleted 'cause I will put them to
another widget later.
Hide all widgets that you want to transfer
Set parent widget for all this widgets to nullptr
Later... set necessary parent and show widgets
Note: if you want to delete widgets inside some slots, you should use deleteLater method.

Related

QT. Add new layout to QLayout

I create interface dynamically when Application is run.
1) I have QTabWidget with 4 predefined tabs. But i must show only 1 or 2 tabs, in case of user shoice. On StackOverflow i learned, that i must keep all tabs in collection to add and destroit it.
I have QHash: twInputMethodsTabs = new QHash< int, QPair<QWidget*, QString> >();
First argument = index; Second = Tab Widget; Third = Tab Widget Caption Text;
2) I fill the collection like this:
for(int i = 0; i < ui->twInputMethods->children().length(); i++)
{
twInputMethodsTabs->insert(i, QPair<QWidget*, QString>(ui->twInputMethods->widget(i), ui->twInputMethods->tabText(i)));
}
3) I add new widget in the tab like this:
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
4) How can i add new layout to this widget? I want to do like this:
QHBoxLayout *hblParams =new QHBoxLayout();
twInputMethodsTabs->value(1).first->layout()->addLayout(hblParams);
But it does not work, because layout() returns QLayout which havent addLayout() function. How i can do this?
Or how can i should change architecture of code to do this?
In this following code you get a widget (.first) and then select that widget's layout ->layout() and then add a Widget to that layout ->addWidget().
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
In the following code you get a widget (.first) and then select that widget's layout ->layout() and try to set the layout on the layout.
twInputMethodsTabs->value(1).first->layout()->addLayout(hblParams);
Replacing the QLayout
To set the layout on the parent widget, you need to drop the ->layout():
twInputMethodsTabs->value(1).first->addLayout(hblParams);
Note that since you are now adding an empty layout to the widget, any widgets current in the previous layout will be lost, so you may need to re-add the widgets to the layout.
Adding new QLayout inside existing QLayout
If you want to add a layout into the existing layout, you cannot do this directly. QLayout can only accept QWidget via .addWidget(). However, you can apply a layout to an empty QWidget() and then add that to the layout. For example:
QWidget *w = new QWidget();
w.addLayout(hblParams);
twInputMethodsTabs->value(1).first->layout()->addWidget(w);
An alternative is to set the layout on the QWidget to a layout that does support .addLayout() such as QHBoxLayout or QVBoxLayout. For example:
QVBoxLayout *l = new QVBoxLayout();
cmbbCommands.setLayout(l); // Now has a layout that supports .addLayout
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
Now the following should work because ->layout() returns a QVBoxLayout:
QHBoxLayout *hblParams =new QHBoxLayout();
twInputMethodsTabs->value(1).first->layout()->addLayout(hblParams);
I Hope, I get what you want to do:
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
QHBoxLayout *hblParams =new QHBoxLayout();
QWidget *w = new QWidget(twInputMethodsTabs->value(1).first);
twInputMethodsTabs->value(1).first->layout()->addWidget(w);
w->addLayout(hblParams);
I just wrote the code here, so it is untested. However it should give you an idea what I tried to explain in my comment.
Cutted from "working" application:
WidgetA::WidgetA(QWidget *parent) : QWidget(parent)
{
QVBoxLayout *pLayout = new QVBoxLayout();
buildWidget(pLayout);
this->setLayout(pLayout);
}
void WidgetA::buildWidget(QVBoxLayout *layout){
for(int i=0; i<4; ++i){
this->buildSegments(layout);
}
}
void WidgetA::buildSegments(QVBoxLayout *layout){
QHBoxLayout *pHbl = new QHBoxLayout();
QLabel *pSegmentSize = new QLabel(this);
pSegmentSize->setText(tr("Segment Size(1,N)"));
QSpinBox *pSegments = new QSpinBox(this);
pHbl->addWidget(pSegmentSize);
pHbl->addWidget(pSegments);
layout->addItem(pHbl);
}
Read this one: Widgets Tutorial - Nested Layouts

Can you hide a QGroupBox frame but preserve it's content visible?

I have a QGroupBox. Depending on the context, it's title may be redundent (displayed in another place of the GUI), so I then need to make as if the QGroupBox was not here....but I must preserve it's content visible (so I don't want to call QGroupBox::hide())!
I need to do this dynamically at runtime and would like to avoid creating/destroying the QGroupBox + reparenting it's content....there must be an easier way to do this.
What I tried so far:
QGroupBox visible:
QGroupBox::setTitle("") removes the text.
QGroupBox::setFlat(true) makes the frame be a single line.
I end up with this:
Not too bad...but a line remains....is there a way to completely hide the QGroupBox frame but preserve it's content visible?
My option:
QGroupBox theBox;
theBox.setFlat(true);
//This removes the border from a QGroupBox named "theBox".
theBox.setStyleSheet("QGroupBox#theBox {border:0;}");
//This removes the border from the group box and all of its children
theBox.setStyleSheet("border:0;");
You can derive your own Group Box from the QGroupBox and reimplement the paintEvent() method. It should be very simple. Original QGroupBox::paintEvent() looks like this:
void QGroupBox::paintEvent(QPaintEvent *)
{
QStylePainter paint(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
paint.drawComplexControl(QStyle::CC_GroupBox, option);
}
What you need to do is just to modify the style option right before the widget is painted:
void CMyGroupBox::paintEvent(QPaintEvent *)
{
QStylePainter paint(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
// This should disable frame painting.
option.features = QStyleOptionFrame::None;
paint.drawComplexControl(QStyle::CC_GroupBox, option);
}
You can use QFrame + QGridLayout (or some more complex combination of layouts) + QSS instead of a QGroupBox.
Considering a QGroupBox only, a trivial solution via QSS could be:
static const char kSavedTitle[] = "_savedTitle";
void hideBoxFrame(QGroupBox * box) {
box->setProperty(kSavedTitle, box->title());
box->setTitle(QString());
box->setStyleSheet("border:none");
}
void showBoxFrame(QGroupBox * box) {
box->setTitle(box->property(kSavedTitle).toString());
box->setStyleSheet(QString());
}
Here's an example that does it by swapping the widgets and reparenting the children. It works for any widget that has direct children, not only QGroupBox. It would require special case handling for widgets such as QScrollArea and QMainWindow that wrap children in a special sub-widget.
See this question for a related discussion of programmatically promoting widgets.
// https://github.com/KubaO/stackoverflown/tree/master/questions/group-reparent-36603051
#include <QtWidgets>
/// Replaces the visible widget with a hidden widget, preserving the layout of the
/// children, and making the new widget visible.
void swapWidgets(QWidget * a, QWidget * b)
{
auto src = a->isVisible() ? a : b;
auto dst = a->isVisible() ? b : a;
Q_ASSERT(dst->isHidden());
/// Move the children to the destination
dst->setLayout(src->layout());
/// Replace source with destination in the parent
auto layout = src->parentWidget()->layout();
delete layout->replaceWidget(src, dst);
/// Unparent the source, otherwise it won't be reinsertable into the parent.
src->setParent(nullptr);
/// Only the destination should be seen.
src->hide();
dst->show();
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
QGridLayout wLayout{&w};
QPushButton swapBtn{"Swap"};
wLayout.addWidget(&swapBtn);
QWidget noBox;
QGroupBox box{"Group"};
wLayout.addWidget(&box);
QGridLayout boxLayout{&box};
for (int i = 0; i < 16; ++i)
boxLayout.addWidget(new QLabel(QString("Tr%1").arg(i)), i/8, i%8);
swapBtn.connect(&swapBtn, &QPushButton::clicked, [&] { swapWidgets(&box, &noBox); });
w.show();
return app.exec();
}
Yes there is a alternative that you can Try.
You can morph into a QFrame which will keep the behavior But make the container boundaryless
You can simply right click on the Group Box in the QDesigner and Select the 'Morph Into' option to select from

Qt: Resizing QMenuBar corner widget

I put a push button into the top-right corner of my main window menu bar:
QPushButton *pb = new QPushButton("Text");
pb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
QMainWindow *mainWindow;
mainWindow->menuBar()->setCornerWidget(pb, Qt::TopRightCorner);
The initial layout is fine. Sometime later, an asynchronous event changes the QPushButton's text to a longer string, but it gets clipped on the right.
I can see that the QPushButton's size changes when the string is updated. The QPushButton is displayed correctly if the window is resized. The problem appears to be getting the QMenuBar to recognize that the widget's size has changed.
This answer How to auto change QPushButton width and QMenuBar corner widget width when change text of button? suggests resetting the corner widget. I would rather avoid that, because my application's structure makes me jump through several ugly and awkward hoops to reset the corner widget after initializing.
The solution is simple. After updating the text of the button call menuBar()->adjustSize(); I have tested it in Qt5.5 and hope it will work for you.
You can use QWidgetAction as a horizontal menu widget:
class TestMenu : public QWidgetAction
{
public:
TestMenu(QObject *parent) :
QWidgetAction (parent)
{
}
virtual QWidget *createWidget(QWidget *parent)
{
QComboBox *combo = new QComboBox(parent);
combo->setFixedWidth(300);
return combo;
}
virtual void deleteWidget(QWidget *widget)
{
delete widget;
}
};
...
QMenu *menu = new QMenu();
menu->addAction(new TestMenu(this));
menuBar()->setCornerWidget(menu);

QAxWidget don’t display

I used QWidget show as a dialog. And put QAxWidget as a subwidget.
But if I set the parent widget transparent, the QAxWidget will not display.
QWidget* widget = new QWidget;
widget->setAttribute(Qt::WA_TranslucentBackground);
......;
QAxWidget* axieBrowser= new QAxWidget(widget);
axieBrowser->setControl(QString::fromUtf8("{8856F961-340A-11D0-A96B-00C04FD705A2}"));
axieBrowser->dynamicCall("Navigate(const QString&)", "www.google.com");
I try if you use other widget, that will display, only QAxWidget don’t display! How could I fi
xed that?
#Dariusz Scharsig I use this function to force the QWidget fresh because repaint() is no effect.
void CMyDialog::changeRectToFresh()
{
this->showNorMal();
QRect rect = this->geometry();
this->setGeometry(rect.adjusted(-1,-1,1,1);
//recover
this->setCeometry(rect);
}

Custom Qt designer widget : a scroll Area containing a custom vertical layout

I want to do something fairly simple : add a custom widget to Qt designer that would basically be a scrollArea containing a custom vertical layout(I added some code to the vertical layout in order to handle its objects for my projects).
The idea would be to represent a vertical menu that would be on the side of my screen
What I have done so far
I created the custom widget plugin and my custom layout.
My custom widget codes looks like this:
#include "menuwidget.h"
MenuWidget::MenuWidget(QWidget *parent) :
QScrollArea(parent)
{
this->setWidgetResizable(true);
QWidget* layoutHoldingWidget= new QWidget(this);
layout= new MenuLayout();
layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
layout->addStretch(1);
layoutHoldingWidget->setLayout(layout);
this->setWidget(layoutHoldingWidget);
}
If I add manually to the layout (in the constructor code) some buttons
for(int i =0;i<20;i++)
layout->addWidget(new QPushButton(this));
It does work and I can see my scrollArea containing some buttons, which is almost what I want.
What I want
I would like to be able to add these buttons directly via Qt designer: the user would first drag the empty MenuWidget on the main window, then would drag QPushButtons on my custom widget exactly like he would do on a regular vertical layout.
Is that possible?How could I do such a thing?
Thank you ! :)
Edit 1
What I was missing was the "scrollAreaWidgetContents" widget that is always created when you drag and drop a QScrollArea. I did a similar thing by adding a widget (let's call it containerWidget) to my custom scrollArea in its domXml function, which enables me to drag and drop widgets on my scroll Area like I wanted to do.
BUT there's still something I can't figure out : I want the containerWidget to have a customLayout (myCustomLayout) . If I add it in the domXml function, I get the following line in the terminal :
Designer:The layout type 'MyCustomLayout' is not supported,
defaulting to grid.
So it means that I can't tell designer to use my custom layout to place my widgets, which is kind of sad :D
Is there any way to "cheat" here?
There are two things to consider:
1) Overwrite in the class you derive from QDesignerCustomWidgetInterface the function to return true
bool isContainer() const { return true; }
This tells QtDesigner that the widget can contain children. (In Qt nearly any Widget can contain any widget as child, but QtDesigner tries to restrict it in a sensible way - e.g. you cant add children to a QLabel in QtDesigner)
2) Implement childEvent of your Widget. Probably in your case it would add Widgets added in QtDesigner to a layout.
Here is a core I've implemented to try this out. I've created a skeleton using "Qt Widget Plugin" Wizard in QtCreator and modified a little bit.
Don't forget to build as release, for the compiler/Qt-version of your QtDesigner , to copy the .dll and .lib files in \plugins\designer directory and to restart QtDesigner!
verticalplugin.cpp
//all other functions remained as created by QtCreator wizard
bool VerticalMenuPlugin::isContainer() const
{
return true;
}
VerticalMenu.h
#ifndef VERTICALMENU_H
#define VERTICALMENU_H
#include <QtGui/QWidget>
#include <QtGui/QVBoxLayout>
class VerticalMenu : public QWidget
{
Q_OBJECT
protected:
virtual void childEvent ( QChildEvent * event );
public:
VerticalMenu(QWidget *parent = 0);
};
#endif
VerticalMenu.cpp
#include "verticalmenu.h"
#include <QChildEvent>
VerticalMenu::VerticalMenu(QWidget *parent) :
QWidget(parent)
{
setLayout (new QVBoxLayout);
}
void VerticalMenu::childEvent ( QChildEvent * event )
{
if ( event->added() )
{
QWidget * newChild = qobject_cast<QWidget *>(event->child());
if ( newChild )
{
layout()->addWidget( newChild );
}
}
}
I hope' it would help as a starting point.
Qt 4 does not support custom layout plugins for designer, so I couldn't achieve what I wanted to do. I will instead use a Vertical Layout and try to implement the additional features that were supposed to be in the custom layout code in the widget code.

Resources