QT - How to place widgets in mutually exclusive groups? - c++

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.

Related

Display text from QLineEdit in a QTextEdit already containing some text and update it in real time

What is the procedure to make a text being written in a QLineEdit widget, dynamically display inside a QTextEdit that already contains some text?
For example, let us say that a QLineEdit asks for a name where one writes "John". Is it possible to display it in real time inside a QTextEdit containing :
The name is + textFromQLineEdit + , age 24 ?
The displayed text has to dynamically take into account the changes being made to the QLineEdit so that the user does not need to press a button or press enter to see his/her name appear.
The following is the minimal code for connecting the two widgets to each other using the signal textChanged() from QLineEdit and the slot setText() from QTextEdit (which does not allow for adding some text before and after the text from the QLineEdit) :
#include <QLineEdit>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextEdit>
#include <QApplication>
class SmallWindow : public QWidget
{
Q_OBJECT
public:
SmallWindow();
private:
QLineEdit *nameLine;
QTextEdit *textBox;
};
SmallWindow::SmallWindow() : QWidget()
{
setFixedSize(300,250);
QLineEdit *nameLine = new QLineEdit;
QTextEdit *textBox = new QTextEdit;
QWidget::connect(nameLine,SIGNAL(textChanged(QString)),textBox,SLOT(setText(QString)));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(nameLine);
layout->addWidget(textBox);
QGroupBox *group = new QGroupBox(this);
group->setLayout(layout);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SmallWindow window;
window.show();
app.exec();
}
#include "main.moc"
What should be done to keep the text before and after the QLineEdit text in place and updating the QTextEdit box in real time?
Create special slot:
void SmallWindow::pasteText(const QString& str)
{
textBox->setText(QString("The name is %1 , age 24").arg(str));
}
and don't use textChanged() signal because you need only one name accepted by user, so you need QLineEdit::editingFinished() (or maybe QLineEdit::returnPressed(), it depends on your needs)
connect(nameLine,SIGNAL(editingFinished(QString)),this,SLOT(pasteText(QString)));
Also you don't need QWidget::connect, because you write this code inside QObject subclass, so it is not necessary.
Also these lines:
QLineEdit *nameLine = new QLineEdit;
QTextEdit *textBox = new QTextEdit;
should be:
nameLine = new QLineEdit;
textBox = new QTextEdit;
Create an own slot for the text update. I think that you have also some errors in your code.
Widgets nameLine and textBox are already defined in the SmallWindow.h. You probably want to create them in SmallWindow.cpp following way:
nameLine = new QLineEdit;
textBox = new QTextEdit;
Also GroupBox group is not set to any layouts. Perhaps you want to create one layout more and set the widget there?
QVBoxLayout *mainlayout = new QVBoxLayout;
mainlayout->addWidget(group);
this->setLayout(mainlayout);
If you create an own slot for the text update, you can just change text content of the textBox there:
SmallWindow.h
#ifndef SMALLWINDOW_H
#define SMALLWINDOW_H
#include <QLineEdit>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextEdit>
class SmallWindow : public QWidget
{
Q_OBJECT
public:
SmallWindow();
private slots:
void updateLineEditText(QString name);
private:
QLineEdit *nameLine;
QTextEdit *textBox;
};
#endif // SMALLWINDOW_H
SmallWindow.cpp
#include "SmallWindow.h"
SmallWindow::SmallWindow() : QWidget()
{
setFixedSize(300,250);
nameLine = new QLineEdit;
textBox = new QTextEdit;
connect(nameLine,SIGNAL(textChanged(QString)),this,
SLOT(updateLineEditText(QString)));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(nameLine);
layout->addWidget(textBox);
QGroupBox *group = new QGroupBox(this);
group->setLayout(layout);
QVBoxLayout *mainlayout = new QVBoxLayout;
mainlayout->addWidget(group);
this->setLayout(mainlayout);
}
void SmallWindow::updateLineEditText(QString name) {
QString textEditString("The name is ");
textEditString.append(name);
textEditString.append(", age 24 ?");
textBox->setText(textEditString);
}
This is a minimal example in Qt 5, using C++11. It is about as concise as it would be in Python. If you are using Qt 5, then your question should have looked exactly as it does below, save for the connect statement. This is what "minimal" means when it comes to Qt. Avoid the fluff and boilerplate that doesn't add to the problem. There's no need to have a separate class for the window in such a simple example.
#include <QVBoxLayout>
#include <QLineEdit>
#include <QTextEdit>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
QVBoxLayout layout(&window);
QLineEdit name;
QTextEdit text;
layout.addWidget(&name);
layout.addWidget(&text);
QObject::connect(&name, &QLineEdit::textChanged, [&](const QString & name){
text.setPlainText(QString("The name is %1, age 24.").arg(name));
});
window.show();
return app.exec();
}
The same in Qt 4 is below - note the absence of any manual memory management. That's how your question ideally should have looked for Qt 4, without the slot's implementation of course. You can use the connectSlotsByName or an explicit connect, of course - that's just a matter of style.
#include <QVBoxLayout>
#include <QLineEdit>
#include <QTextEdit>
#include <QApplication>
class Window : public QWidget {
Q_OBJECT
QVBoxLayout m_layout; // not a pointer!
QLineEdit m_name; // not a pointer, must come after the layout!
QTextEdit m_text;
Q_SLOT void on_name_textChanged(const QString & name) {
m_text.setPlainText(QString("The name is %1, age 24.").arg(name));
}
public:
Window() : m_layout(this) {
m_layout.addWidget(&m_name);
m_layout.addWidget(&m_text);
m_name.setObjectName("name");
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
#include "main.moc"

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();
}

Segmentation Fault while dynamicaly adding widget in Qt

I was trying to add a QWidget while runtime in Qt but It is showing SIGSEV signal received from OS because of segmentation fault.
Here is my code:
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_submit_clicked();
private:
Ui::MainWindow *ui;
QLabel *label;
QLineEdit *line_edit;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_submit_clicked()
{
QString str = ui->lineEdit1->text();
QString str1 =ui->lineEdit2->text();
if(str=="rana"&&str1=="vivek")
{
label = new QLabel();
label->setText("Success");
MainWindow.layout->addWidget(label);
label->show();
}
else
{
line_edit = new QLineEdit();
line_edit->setText("Sorry");
MainWindow.layout->addWidget(line_edit);
line_edit->show();
}
}
//main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I know that segmentation fault occurs due to dereferencing of a null pointer but i couldn't find where I have done that mistake.Any Suggestions?
MainWindow.layout->addWidget(label);
doesn't make a lot of sense - this should not even compile, as Sebastian noted.
First, make sure you have layout in the Ui file (I added one vertical layout named verticalLayout), so you have a layout where you will add widgets. You will have a pointer to it inside your ui object.
Now, just use addWidget on that layout and everything should work:
void MainWindow::on_pushButton_submit_clicked()
{
QString str = ui->lineEdit1->text();
QString str1 =ui->lineEdit2->text();
if(str=="rana"&&str1=="vivek")
{
QLabel *label = new QLabel();
label->setText("Success");
ui->verticalLayout->addWidget(label);
// label->show(); widgets will became the part of the MainWindow, as the addWidget
// will add them into the hierarchy.
}
else
{
QLineEdit *line_edit = new QLineEdit();
line_edit->setText("Sorry");
ui->verticalLayout->addWidget(line_edit);
// line_edit->show()
}
}
Note - addWidget will set the owner of the widget to be the layout, so the widget will be deleted on the destruction of the layout.
Maybe implementing in this way will make sense?
void MainWindow::on_pushButton_submit_clicked()
{
QString str = ui->lineEdit1->text();
QString str1 =ui->lineEdit2->text();
QWidget *w = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout; // creates a vertical layout
if(str=="rana"&&str1=="vivek")
{
label = new QLabel(w);
label->setText("Success");
layout->addWidget(label);
}
else
{
line_edit = new QLineEdit(w);
line_edit->setText("Sorry");
layout->addWidget(line_edit);
}
w->setLayout(layout);
setCentralWidget(w);
}
UPDATE:
QMainWindow already has a predefined layout, so it was needless to introduce a new one. The code above creates an intermediate widget and construct it using its own layout. Than the widget set as a central widget in the MainWindow.

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 separator widget?

Greetings all,
Is there any widget to separate two QWidgets and also give full focus to a one widget.
As shown in following figure ?
Thanks in advance,
umanga
How about QSplitter?
QWidget 1, for exmaple, QListView. QWidget 2 is a combination of QWidgets (the left part is simple QPushButton with show/hide caption, and the right part another widget)... All you have to do, is to hide your QWidget2 when user clicked on QPushButton...
If you need an example, I may post it.
Updated
main.cpp
#include "splitter.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
splitter w;
w.show();
return a.exec();
}
splitter.h
#ifndef SPLITTER_H
#define SPLITTER_H
#include <QtGui/QDialog>
class splitter : public QDialog
{
Q_OBJECT;
QWidget* widget1;
QWidget* widget2;
QPushButton* button;
public:
splitter(QWidget *parent = 0, Qt::WFlags flags = 0);
~splitter();
private slots:
void showHide(void);
};
#endif // SPLITTER_H
splitter.cpp
#include <QtGui>
#include "splitter.h"
splitter::splitter(QWidget *parent, Qt::WFlags flags)
: QDialog(parent, flags)
{
QApplication::setStyle("plastique");
QListView* listView = new QListView;
QTableView* tableView = new QTableView;
button = new QPushButton("Hide >");
widget1 = new QWidget;
QHBoxLayout* w1Layout = new QHBoxLayout;
w1Layout->addWidget(listView);
w1Layout->addWidget(button);
widget1->setLayout(w1Layout);
widget2 = new QWidget;
QHBoxLayout* w2Layout = new QHBoxLayout;
w2Layout->addWidget(tableView);
widget2->setLayout(w2Layout);
QSplitter *mainSplitter = new QSplitter(this);
mainSplitter->addWidget(widget1);
mainSplitter->addWidget(widget2);
connect(button, SIGNAL(clicked()), this, SLOT(showHide()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(mainSplitter);
setLayout(mainLayout);
}
splitter::~splitter()
{}
void splitter::showHide(void)
{
if (widget2->isVisible())
{ // hide
widget2->setVisible(false);
button->setText("< Show");
}
else
{ // show
widget2->setVisible(true);
button->setText("Hide >");
}
}