Qt not removing item from layout - c++

I have a simple Qt application that dynamically adds and deletes items from the layout, but it does not work, so I wrote the code below to illustrate what I'm doing:
The header file justs put an additional layout field in addition to Qt creator template:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFrame>
#include <QVBoxLayout>>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QFrame* create_frame();
void remove_frame();
QVBoxLayout* layout;
};
#endif // MAINWINDOW_H
The cpp file adds a few QFrame to the current layout, then clicking the button will remove them one by one. The QCoreApplication::processEvents and QThread::sleep just adds some animation to show what is happening.
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QPushButton>
#include <QThread>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto window = new QWidget(this);
setCentralWidget(window);
auto button = new QPushButton(window);
button->setGeometry(100, 100, 100, 100);
layout = new QVBoxLayout;
window->setLayout(layout);
for (int i = 0; i < 5; i++) {
auto x = create_frame();
layout->addWidget(x);
}
connect(button, &QPushButton::clicked, this, &MainWindow::remove_frame);
}
void MainWindow::remove_frame() {
while(layout->count() > 0) {
auto item = layout->itemAt(0);
layout->removeItem(item);
delete item;
QCoreApplication::processEvents();
QThread::msleep(200);
}
}
QFrame* MainWindow::create_frame() {
auto x = new QFrame();
x->setMinimumHeight(20);
x->setMaximumHeight(20);
x->setFrameStyle(QFrame::StyledPanel);
return x;
}
MainWindow::~MainWindow()
{
delete ui;
}
The animation is a mess, and does not completely remove all the QFrame boxes. What did I do wrong here? And later I find that if I use auto item = layout->itemAt(0)->widget(); layout->removeWidget(item); then I can indeed remove them, are removeItem and removeWidget doing different things?

Related

Widget is not properly visible

In this code which can able to play video and play *.mp3. code works properly,in my mainwindow.ui I added widget called widgetGif by drag and drop .This widget containing label also. but this widget not visible when I run the program. How can I display this widget called widgetGif
here is part of my code :
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QMovie>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
player = new QMediaPlayer(this);
vw = new QVideoWidget (this);
player->setVideoOutput(vw);
this->setCentralWidget(vw); //I think this is the reason
slider = new QSlider(this);
bar = new QProgressBar(this);
slider->setOrientation(Qt::Horizontal);
ui->statusBar->addPermanentWidget(slider);
ui->statusBar->addPermanentWidget(bar);
connect(player,&QMediaPlayer::durationChanged,slider,&QSlider::setMaximum);
connect(player,&QMediaPlayer::positionChanged,slider,&QSlider::setValue);
connect(slider,&QSlider::sliderMoved,player,&QMediaPlayer::setPosition);
connect(player,&QMediaPlayer::durationChanged,bar,&QProgressBar::setMaximum);
connect(player,&QMediaPlayer::positionChanged,bar,&QProgressBar::setValue);
sliderVolumn = new QSlider(this);
sliderVolumn->setOrientation(Qt::Horizontal);
ui->statusBar->addPermanentWidget(sliderVolumn);
connect(sliderVolumn,&QSlider::sliderMoved,player,&QMediaPlayer::setVolume);
QMovie *movie=new QMovie(":/res/icons/giphy.gif");
if (!movie->isValid())
{
// Something went wrong :(
}
ui->labelGif->setMovie(movie);
movie->start();
ui->widgetGif->setVisible(true);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionOpen_triggered()
{
QString filename= QFileDialog::getOpenFileName(this,"Open Folder","","Open a File(*.*)");
on_actionStop_triggered();
player->setMedia(QUrl::fromLocalFile(filename));
on_actionPlay_triggered();
if(filename.endsWith(".mp3")){
qDebug() << " file is mp3";
}else{
qDebug() << " not is mp3";
}
}
QMainWindow needs a centralWidget. You must use layouts to include your QVideoWidget and also your widgetGif and then set it as centralWidget of the QMainWindow.
In the following example, textEdit would be your QVideoWidget and the object label is like your widgetGif.
We have also two buttons to hide or show label using the method setVisible.
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class QLabel;
class QPushButton;
class QTextEdit;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QWidget *centralWidget;
QLabel *label;
QPushButton *pushButton;
QPushButton *pushButton_2;
QTextEdit *textEdit;
public slots:
void showSlot();
void hideSlot();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QTextEdit>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Vertical layout
QVBoxLayout *mainLayout = new QVBoxLayout;
// Widgets
centralWidget = new QWidget(this);
label = new QLabel();
pushButton = new QPushButton();
pushButton_2 = new QPushButton();
textEdit = new QTextEdit();
// Title and texts
this->setWindowTitle("MainWindow");
label->setText("TextLabel");
pushButton->setText("Show");
pushButton_2->setText("Hide");
// Add widgets to layout
mainLayout->addWidget(textEdit);
mainLayout->addWidget(label);
mainLayout->addWidget(pushButton);
mainLayout->addWidget(pushButton_2);
centralWidget->setLayout(mainLayout);
// Set the central widget
this->setCentralWidget(centralWidget);
connect(pushButton, SIGNAL (clicked()), this, SLOT (showSlot()));
connect(pushButton_2, SIGNAL (clicked()), this, SLOT (hideSlot()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showSlot()
{
label->setVisible(true);
}
void MainWindow::hideSlot()
{
label->setVisible(false);
}

QT - connecting wrong button from member QWidget

I have a main window which opens a new window and connects a button from the new window to a "close" function. The problem arises when that new window has more than one button; it will always connect the last created button instead of the explicited one. Here is a sample working code:
"main.cpp"
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
"mainwindow.h"
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "screen_char_info.h"
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
QPushButton *button_show_char_info;
Screen_Char_Info *screen_char_info;
QWidget *mainwidget;
QVBoxLayout *layout_main;
MainWindow(QWidget *parent = 0) : QMainWindow(parent) {
button_show_char_info = new QPushButton("Character info", this);
layout_main = new QVBoxLayout();
mainwidget = new QWidget(this);
screen_char_info = NULL;
QObject::connect (button_show_char_info, &QPushButton::clicked, [this]{
if (screen_char_info == NULL) {
screen_char_info = new Screen_Char_Info();
screen_char_info->show();
QObject::connect (screen_char_info->button_return, &QPushButton::clicked, [=] {
screen_char_info->close();
screen_char_info = NULL;
});
}
});
layout_main->addWidget(button_show_char_info);
mainwidget->setLayout(layout_main);
setCentralWidget(mainwidget);
}
~MainWindow()
{
}
};
#endif // MAINWINDOW_H
"screen_char_info.h"
#ifndef SCREEN_CHAR_INFO_H
#define SCREEN_CHAR_INFO_H
#include <QString>
#include <QMenu>
#include <QMenuBar>
#include <QLabel>
#include <QTextEdit>
#include <QPushButton>
#include <QWidget>
#include <QLineEdit>
#include <QGridLayout>
class Screen_Char_Info : public QWidget {
Q_OBJECT
public:
QPushButton *buttons_modify_attributes[15];
QPushButton *button_return;
QGridLayout *layout;
Screen_Char_Info () {
this->setAttribute(Qt::WA_DeleteOnClose);
this->setWindowTitle("Character Info");
layout = new QGridLayout(this);
for (int i = 0; i <= 15; i++) {
buttons_modify_attributes[i] = new QPushButton((i%2 ? "-" : "+"), this);
connect(buttons_modify_attributes[i], &QPushButton::clicked, [this] {
});
layout->addWidget(buttons_modify_attributes[i], (i / 2), (i % 2), 1, 1);
}
layout->addWidget(button_return = new QPushButton("Return", this), 8, 0, 1, 1);
this->setLayout(layout);
}
};
#endif // SCREEN_CHAR_INFO_H
However, if i put the line layout->addWidget(button_return... before the for loop, the button that closes the window is the last "-" button, and not the return one.
The way you do the connect does not appear to be conventional. Try using traditional Qt way:
connect(pButtonToPress, SIGNAL(clicked()), pObjectToHandle, SLOT(onClicked));
Provided QPushButton* pButtonPress actually pointing to QPushButton and pObjectToHandle to some object (can be 'this' pointer):
class ObjHandler
{
public slot:
void onClicked();
};
... should satisfy. SIGNAL and SLOT are macro that work with some help of Qt meta object compiler. That is why having slot: statement is highly critical.
Found the bug, I was declaring a button matrix with 15 elements, but iterating over a 16 element loop. The 16th element was the return button, and was overwritten in the loop.

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.

QtPushButton wont click

Hello Im working through Qt tutorials I've have copy the code for the communicate section of this tutorial. the code compiles and shows but none of my buttons are clickable.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QPushButton;
class QLabel;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void OnPlus();
void OnMinus();
private:
Ui::MainWindow *ui;
QLabel *label;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtGui>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QPushButton *plus = new QPushButton("+", this);
plus->setGeometry(50, 40, 75, 30);
QPushButton *minus = new QPushButton("-", this);
minus->setGeometry(50, 100, 75, 30);
label = new QLabel("0", this);
label->setGeometry(190, 80, 20, 30);
connect(plus, SIGNAL(clicked()), this, SLOT(OnPlus()));
connect(minus, SIGNAL(clicked()), this, SLOT(OnMinus()));
ui->setupUi(this);
}
void MainWindow::OnPlus()
{
int val = label->text().toInt();
val++;
label->setText(QString::number(val));
}
void MainWindow::OnMinus()
{
int val = label->text().toInt();
val--;
label->setText(QString::number(val));
}
MainWindow::~MainWindow()
{
delete ui;
}
main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QPushButton>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
Your problem is with this line:
ui->setupUi(this);
It creates an invisible central widget for your main window, which blocks all events destined for your buttons, which is why they don't depress when you click them. Move this line to the beginning of your constructor for MainWindow and the problem will go away.

Qt Creating layouts and adding widgets to layouts dynamically

I am trying to create layouts in my MainWindow class dynamically. I have four frames which are laid with a grid layout object. Each frame contains a custom ClockWidget. I want the ClockWidget objects to resize accordingly when I resize the main window, so I need to add them to a layout. However, I need to do this at runtime, since the object itself is created at runtime. I tried to accomplish this programmatically, but the commented-out code below attempting to create a new layout causes the program to crash. What is the procedure for doing this correctly?
Header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "ClockView.h"
namespace Ui{
class MainWindow;
}
class QLayout;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void populateViewGrid();
private:
Ui::MainWindow *ui;
ClockView *clockView_1;
ClockView *clockView_2;
ClockView *clockView_3;
ClockView *clockView_4;
QLayout *layout_1;
QLayout *layout_2;
QLayout *layout_3;
QLayout *layout_4;
};
#endif // MAINWINDOW_H
implementation file:
#include <QVBoxLayout>
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
populateViewGrid();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::populateViewGrid()
{
clockView_1 = new ClockView(ui->frame_1);
clockView_2 = new ClockView(ui->frame_2);
clockView_3 = new ClockView(ui->frame_3);
clockView_4 = new ClockView(ui->frame_4);
/*
layout_1 = new QVBoxLayout;
layout_2 = new QVBoxLayout;
layout_3 = new QVBoxLayout;
layout_4 = new QVBoxLayout;
layout1->addWidget(clockView_1);
layout2->addWidget(clockView_2);
layout3->addWidget(clockView_3);
layout4->addWidget(clockView_4);
ui->frame_1->setLayout(layout_1);
ui->frame_2->setLayout(layout_2);
ui->frame_3->setLayout(layout_3);
ui->frame_3->setLayout(layout_4);
*/
}
Your procedure is correct. There are some typos, for example, you're setting the layout twice for frame3. That may be your problem. Crashes aren't always reproducible. I don't think you have any other problems than that. Below is a self contained example. It also keeps all the instances by value, avoiding the premature pessimization of an extra dereference via a pointer.
// https://github.com/KubaO/stackoverflown/tree/master/questions/dynamic-widget-10790454
#include <cmath>
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <array>
// Interface
class ClockView : public QLabel
{
public:
explicit ClockView(QWidget* parent = nullptr) : QLabel(parent)
{
static int ctr = 0;
setText(QString::number(ctr++));
}
};
class MainWindow : public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = nullptr);
void populateViewGrid();
private:
static constexpr int N = 10;
QWidget central{this};
QGridLayout centralLayout{&central};
std::array<QFrame, N> frames;
std::array<ClockView, N> clockViews;
std::array<QVBoxLayout, N> layouts;
};
// Implementation
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setCentralWidget(&central);
const int n = ceil(sqrt(N));
for (int i = 0; i < N; ++ i) {
frames[i].setFrameShape(QFrame::StyledPanel);
centralLayout.addWidget(&frames[i], i/n, i%n, 1, 1);
}
populateViewGrid();
}
void MainWindow::populateViewGrid()
{
for (int i = 0; i < N; ++ i) {
layouts[i].addWidget(&clockViews[i]);
frames[i].setLayout(&layouts[i]);
}
}
int main(int argc, char** argv)
{
QApplication app{argc, argv};
MainWindow w;
w.show();
return app.exec();
}
And the qmake project file.
greaterThan(QT_MAJOR_VERSION, 4) {
QT = widgets
CONFIG += c++11
} else {
QT = gui
unix:QMAKE_CXXFLAGS += -std=c++11
macx {
QMAKE_CXXFLAGS += -stdlib=libc++
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7
}
}
TARGET = dynamic-widget-10790454
TEMPLATE = app
SOURCES += main.cpp