Creating and laying out widgets using a for loop in Qt - c++

So i wanted to create 5 buttons in Qt but instead, create just one button and put it in a for loop so i don't have to create each of the 5 buttons manually. I tried different ways but all proved futile. I'm new to C++ and Qt.
Here are the codes;
show.h
#ifndef SHOW_H
#define SHOW_H
#include <QDialog>
#include <QPushButton>
#include <QVBoxLayout>
class Show : public QDialog {
Q_OBJECT
public:
explicit Show(QWidget *parent = 0);
~Show();
private:
QPushButton *button;
};
#endif // SHOW_H
show.cpp
#include "show.h"
#include "ui_show.h"
Show::Show(QWidget *parent) : QDialog(parent) {
int a = 5;
button = new QPushButton[a];
button->setText("Ok");
QVBoxLayout *layout = new QVBoxLayout[a];
for (int i = 0; i < sizeof(button)/4; i++) {
/*here, i wanted to do something like this;
'layout[i].addWidget(button[i]);' but didn't work*/
layout[i].addWidget(button);
}
setLayout(layout);
}
Show::~Show() {
}
main.cpp
#include "show.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Show *dialog = new Show;
dialog->show();
return a.exec();
}
After i run the code, i only see one button.
Your help is deeply appreciated. Thank you!!

QPushButton* pButton = new QPushButton("Ok");
This creates a single instance of a QPushButton.
You can add the button to a layout with a call to Layout::addWidget, which internally calls addItem.
As the documentation states for addItem: -
Note: The ownership of item is transferred to the layout, and it's the layout's responsibility to delete it
So your current code creates a single button and as it gets added to each successive layout, it is removed from the layout in which it was previously added.
You're creating one button and adding the same button 5 times. If you want 5 buttons in 5 layouts using a loop, then you need 5 separate instances of the button: -
for (int i=0; i<5; ++i)
{
QPushButton* pButton = new QPushButton("Ok");
layout[i].addWidget(pButton);
}

In your show.cpp change the lines
Show::Show(QWidget *parent) : QDialog(parent) {
int a = 5;
// You only need one layout for all buttons, not one per button.
QVBoxLayout *layout = new QVBoxLayout( this );
for (int i = 0; i < a; i++) {
QPushButton * newButton = new QPushButton( this );
newButton ->setText( "Ok" );
layout->addWidget( newButton );
}
setLayout(layout);
}
Show::~Show() {
}
The layout expects a pointer and you only need one layout instead one per button.

Related

Need help when autohiding a tabBar in Qt with C++

I am new to Qt programming in C++. I am using Visual Studio 2019 as editor. I made two buttons (b1 and b2) and a tab widgets. They are visible properly in the UI as shown in the following Fig.
What I want? I want to auto hide the tab area.
What I did? This is the code that contains the widgets (tab and buttons).
main.cpp
#include "tabbar.h"
#include <QtWidgets/QApplication>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
TabBar w;
w.show();
return a.exec();
}
tabbar.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_tabbar.h"
#include<qtabbar.h>
#include<qpushbutton.h>
#include<qgridlayout.h>
#include<qdebug.h>
class TabBar : public QMainWindow
{
Q_OBJECT
public:
TabBar();
};
tabbar.cpp
#include "tabbar.h"
TabBar::TabBar()
{
QPushButton* b1 = new QPushButton("b1");
QPushButton* b2 = new QPushButton("b2");
QTabBar* tabBar = new QTabBar;
tabBar->addTab("Tab 1");
tabBar->addTab("Tab 2");
tabBar->setTabText(0, "Hello tab 1");
tabBar->setTabText(1, "Hello tab 2");
QString str;
str = tabBar->tabText(1);
qDebug() << str;
tabBar->setTabToolTip(0, "ToolTip for tab 1");
tabBar->setTabToolTip(1, "ToolTip for tab 2");
tabBar->autoHide();
QGridLayout* layout = new QGridLayout;
layout->addWidget(b1, 0, 0);
layout->addWidget(b2, 0, 1);
layout->addWidget(tabBar, 0, 2);
QWidget* wid = new QWidget;
wid->setLayout(layout);
setCentralWidget(wid);
}
The main problem: Even though I call the function autoHide() through tabBar() function the tab are is not hidden. Did I miss something?
the method you are using is actually the "getter" of the property, you need to call the setter setAutoHide(bool hide), furthermore you should read the doc:
autoHide : bool
If true, the tab bar is automatically hidden when it contains less than 2 tabs.
so with 2 tabs nothing is going to happen

QT - How to place widgets in mutually exclusive groups?

I'm looking to do something like the following:
example
But with radio buttons instead of checkboxes. In the above picture widgets are placed into groups which can be enabled/disabled, but I want only 1 group to be enabled at a time (in the example both Group A and Group C are enabled).
I did the above example using QGroupBox, but it only provides normal checkboxes, not radio buttons as far as I can tell.
The ckeckbox that appears in the QGroupBox is not a QCheckBox, it is just a drawing. So a possible solution is to create a class that manages the QGroupBox checked.
#include <QApplication>
#include <QGroupBox>
#include <QLineEdit>
#include <QRadioButton>
#include <QSlider>
#include <QVBoxLayout>
class ExclusiveManager: public QObject{
public:
using QObject::QObject;
void addGroupBox(QGroupBox *groupbox){
if(groupbox){
groupbox->blockSignals(true);
groupbox->setChecked(m_groupboxs.isEmpty());
groupbox->blockSignals(false);
m_groupboxs << groupbox;
connect(groupbox, &QGroupBox::toggled, this, &ExclusiveManager::onToggled);
}
}
private slots:
void onToggled(bool on){
QGroupBox *groupbox = qobject_cast<QGroupBox *>(sender());
if(on){
for(QGroupBox *g: m_groupboxs){
if(g != groupbox && g->isChecked()){
g->blockSignals(true);
g->setChecked(false);
g->blockSignals(false);
}
}
}
else{
groupbox->blockSignals(true);
groupbox->setChecked(false);
groupbox->blockSignals(false);
}
}
private:
QList<QGroupBox *> m_groupboxs;
};
class Widget: public QWidget{
public:
Widget(QWidget *parent=nullptr):QWidget(parent){
setLayout(new QVBoxLayout);
ExclusiveManager *manager = new ExclusiveManager(this);
group_a = new QGroupBox("Group A");
group_a->setCheckable(true);
group_b = new QGroupBox("Group B");
group_b->setCheckable(true);
group_c = new QGroupBox("Group C");
group_c->setCheckable(true);
layout()->addWidget(group_a);
layout()->addWidget(group_b);
layout()->addWidget(group_c);
manager->addGroupBox(group_a);
manager->addGroupBox(group_b);
manager->addGroupBox(group_c);
QVBoxLayout *layA = new QVBoxLayout();
layA->addWidget(new QLineEdit);
group_a->setLayout(layA);
QVBoxLayout *layB = new QVBoxLayout();
layB->addWidget(new QRadioButton("Option 1"));
layB->addWidget(new QRadioButton("Option 2"));
group_b->setLayout(layB);
QVBoxLayout *layC = new QVBoxLayout();
layC->addWidget(new QSlider(Qt::Horizontal));
group_c->setLayout(layC);
}
private:
QGroupBox *group_a;
QGroupBox *group_b;
QGroupBox *group_c;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
You can link each QGroupBox'toggled signal with the other's setDisabled slot.
ps: I know it's too late but I'm just putting it here if someone else needs it.

Qt custom widget not showing child widgets

I have a custom widget with some standard child widgets inside. If I make a separate test project and redefine my custom widget to inherit QMainWindow, everything is fine. However, if my custom widget inherits QWidget, the window opens, but there are no child widgets inside.
This is the code:
controls.h:
#include <QtGui>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QPushButton>
class Controls : public QWidget
{
Q_OBJECT
public:
Controls();
private slots:
void render();
private:
QWidget *frame;
QWidget *renderFrame;
QVBoxLayout *layout;
QLineEdit *rayleigh;
QLineEdit *mie;
QLineEdit *angle;
QPushButton *renderButton;
};
controls.cpp:
#include "controls.h"
Controls::Controls()
{
frame = new QWidget;
layout = new QVBoxLayout(frame);
rayleigh = new QLineEdit;
mie = new QLineEdit;
angle = new QLineEdit;
renderButton = new QPushButton(tr("Render"));
layout->addWidget(rayleigh);
layout->addWidget(mie);
layout->addWidget(angle);
layout->addWidget(renderButton);
frame->setLayout(layout);
setFixedSize(200, 400);
connect(renderButton, SIGNAL(clicked()), this, SLOT(render()));
}
main.cpp:
#include <QApplication>
#include "controls.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Controls *controls = new Controls();
controls->show();
return app.exec();
}
This opens up a window with correct dimensions, but with no content inside.
Bear in mind this is my first day using Qt. I need to make this work without inheriting QMainWindow because later on I need to put this on a QMainWindow.
You're missing a top level layout:
Controls::Controls()
{
... (yoour code)
QVBoxLayout* topLevel = new QVBoxLayout(this);
topLevel->addWidget( frame );
}
Or, if frame is not used anywhere else, directly:
Controls::Controls()
{
layout = new QVBoxLayout(this);
rayleigh = new QLineEdit;
mie = new QLineEdit;
angle = new QLineEdit;
renderButton = new QPushButton(tr("Render"));
layout->addWidget(rayleigh);
layout->addWidget(mie);
layout->addWidget(angle);
layout->addWidget(renderButton);
setFixedSize(200, 400);
connect(renderButton, SIGNAL(clicked()), this, SLOT(render()));
}
Note that setLayout is done automatically when QLayout is created (using parent widget)
You'll want to set a layout on your Controls class for managing its child sizes. I'd recommend removing your frame widget.
controls.cpp
Controls::Controls()
{
layout = new QVBoxLayout(this);
.
.
.
}
main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}

Letting QWidget and QVBoxLayout automatically resize if child resizes (Qt4)

I have the following minimal example code given.
main.cpp:
#include <QApplication>
#include "qt.h"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyDialog mainWin;
mainWin.show();
return app.exec();
}
qt.cpp:
#include <QLabel>
#include "qt.h"
void MyDialog::setupUi()
{
setCentralWidget(new QWidget);
mainLayout = new QVBoxLayout( centralWidget() );
centralWidget()->setLayout(mainLayout);
// show the add new effect channel button
QPushButton* newKnobBtn = new QPushButton("new", this );
connect( newKnobBtn, SIGNAL(clicked()), this, SLOT(addNewKnob()));
mainLayout->addWidget( newKnobBtn, 0, Qt::AlignRight );
containerWidget = new QWidget(this);
scrollArea = new QScrollArea(containerWidget);
mainLayout->addWidget(containerWidget);
scrollLayout = new QVBoxLayout(scrollArea);
scrollArea->setLayout(scrollLayout);
/*
QSizePolicy pol;
pol.setVerticalPolicy(QSizePolicy::Expanding);
setSizePolicy(pol);
*/
addNewKnob(); // to fit size initially
}
void MyDialog::addNewKnob()
{
scrollLayout->addWidget(new QLabel("Hello World", this));
/*
containerWidget->adjustSize();
adjustSize();
*/
}
qt.h:
#include <QMainWindow>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QPushButton>
class MyDialog : public QMainWindow
{
Q_OBJECT
private slots:
void addNewKnob();
private:
void setupUi();
QVBoxLayout* mainLayout;
QScrollArea* scrollArea;
QVBoxLayout* scrollLayout;
QWidget* containerWidget;
public:
MyDialog( ) { setupUi(); }
};
Compiling: Put all in one directory, type
qmake -project && qmake && make
I have the adjustSize() solution from here, but it does not work: (link). Everything I commented out was things I tried but did not help.
How do I make containerWidget and scrollLayout grow correctly, when a new Label is being added to scrollLayout?
Here's a simplified version that works for me:
qt.cpp:
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include "qt.h"
MyDialog::MyDialog()
{
QWidget * mainWidget = new QWidget;
QBoxLayout * mainLayout = new QVBoxLayout(mainWidget);
setCentralWidget(mainWidget);
// show the add new effect channel button
QPushButton* newKnobBtn = new QPushButton("new");
connect( newKnobBtn, SIGNAL(clicked()), this, SLOT(addNewKnob()));
mainLayout->addWidget( newKnobBtn, 0, Qt::AlignRight );
QScrollArea * scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
mainLayout->addWidget(scrollArea);
QWidget * labelsWidget = new QWidget;
labelsLayout = new QVBoxLayout(labelsWidget);
scrollArea->setWidget(labelsWidget);
addNewKnob(); // to fit size initially
}
void MyDialog::addNewKnob()
{
labelsLayout->addWidget(new QLabel("Hello World"));
}
qt.h:
#include <QMainWindow>
#include <QBoxLayout>
class MyDialog : public QMainWindow
{
Q_OBJECT
public:
MyDialog( );
private slots:
void addNewKnob();
private:
QBoxLayout * labelsLayout;
};
You have containerWidget that contain only one QScrollArea. I don't know why do you need this. But if you need this for some reason, you need to add a layout to this widget in order to make layouts work. Also do not create a layout for QScrollArea. It already have internally implemented layout. You should add scrollLayout to the scroll area's viewport() widget instead.
When you construct a layout and pass a widget to its constructor, the layout is automatically assigned to the passed widget. You should not call setLayout after that. This action will take no effect and produce console warning.

Qt Runtime Error when using a derived class

I Simply want to make a program in Qt, where you press one of two buttons and the text of a QLabel changes depending on the button you've changed. I am getting a runtime error when running the script. I made a "custom" window class for this program:
This is the header file:
#ifndef MW_H
#define MW_H
#include <QString>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>
#include <QDialog>
class MW: public QDialog
{
Q_OBJECT
private:
QPushButton* one;
QPushButton* two;
QLabel* three;
QGridLayout* mainL;
public:
MW();
private slots:
void click_1();
void click_2();
};
#endif // MW_H
This is the .cpp for the header:
#include "MW.h"
MW :: MW()
{
//create needed variables
QGridLayout* mainL = new QGridLayout;
QPushButton* one = new QPushButton("Set1");
QPushButton* two = new QPushButton("Set2");
QLabel* three = new QLabel("This text will be changed");
//connect signals and slots
connect(one, SIGNAL(clicked()), this, SLOT(click_1()));
connect(two, SIGNAL(clicked()), this, SLOT(click_2()));
// create layout
mainL->addWidget(one, 1, 0);
mainL->addWidget(two, 1, 1);
mainL->addWidget(three, 0, 1);
setLayout(mainL);
}
void MW :: click_1()
{
three->setText("One Clicked me!");
}
void MW :: click_2()
{
three->setText("Two Clicked me!");
}
And finally this is the main function:
#include <QApplication>
#include "MW.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MW w;
w.setAttribute(Qt::WA_QuitOnClose);
w.show();
return a.exec();
}
This is the third or so small learning program I am doing and I am getting stuck at the same problem. It is starting to get a bit annoying. Any help will be appreciated.
The error rests within your constructor.
QLabel* three = new QLabel("This text will be changed");
This line stores the new QLabel to a local variable instead of the class variable.
As such, your class variable three remains empty. (As do the other three variables, but that's not the issue here, since you don't access them outside of the constructor)
To make long things short, amend your code like this:
MW :: MW()
{
//create needed variables
mainL = new QGridLayout;
one = new QPushButton("Set1");
two = new QPushButton("Set2");
three = new QLabel("This text will be changed"); //This line, actually.
//connect signals and slots
connect(one, SIGNAL(clicked()), this, SLOT(click_1()));
connect(two, SIGNAL(clicked()), this, SLOT(click_2()));
// create layout
mainL->addWidget(one, 1, 0);
mainL->addWidget(two, 1, 1);
mainL->addWidget(three, 0, 1);
setLayout(mainL);
}
Like this, the variables in the class will be filled and your code should work as expected.
Your problem is this:
QGridLayout* mainL = new QGridLayout;
QPushButton* one = new QPushButton("Set1");
QPushButton* two = new QPushButton("Set2");
QLabel* three = new QLabel("This text will be changed");
You're creating four new variables with the same name as your class members. These new variables hide the class member. So with the above code, you never initialize MW::three in particular. When your slot is called, three->setText(...) dereferences an uninitialized pointer and stuff breaks.
Replace that code with:
mainL = new QGridLayout;
one = new QPushButton("Set1");
two = new QPushButton("Set2");
three = new QLabel("This text will be changed");