Clearing and repopulating a QFormLayout - c++

I'm trying to set up a QFormLayout so that some buttons show or hide the part below them, and using setHidden on the widgets I want to hide is resulting in a bad layout due to the form padding still showing.
So I tried this:
#include <QApplication>
#include <QCheckBox>
#include <QLabel>
#include <QFormLayout>
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
QCheckBox checkA;
QFormLayout * m_formLayout;
QWidget * m_widget;
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent),
m_formLayout(0L),
m_widget(0L)
{
populate();
connect(&checkA, &QCheckBox::stateChanged, this, &MainWindow::populate);
}
virtual ~MainWindow() { clear(); }
bool doesOwnObject(void * it) const
{
return (uintptr_t)this <= (uintptr_t)it && (uintptr_t)it < (uintptr_t)(this+1);
}
void clear()
{
if(m_formLayout)
{
QLayoutItem *child;
while ((child = m_formLayout->takeAt(0)) != 0)
{
QLayout * layout = child->layout();
QSpacerItem * spacer = child->spacerItem();
QWidget * widget = child->widget();
if(layout && !doesOwnObject(layout)) delete layout;
if(spacer && !doesOwnObject(spacer)) delete spacer;
if(widget && !doesOwnObject(widget)) delete widget;
}
delete m_formLayout;
m_formLayout = 0L;
}
}
void populate()
{
if(m_widget) { clear(); return; }
m_widget = new QWidget(this);
setCentralWidget(m_widget);
m_formLayout = new QFormLayout(m_widget);
m_formLayout->addRow(tr("Show Check Box B:"), &checkA);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
But that doesn't seem to work as clear() doesn't seem to be actually depopulating the form layout. The widgets remain where they were before the QFormLayout was deleted. And if m_widget is deleted then the program will crash as it tries to delete checkA, because checkA has not been removed from the now deleted form.
How do I fix this?

I have come to a solution that has the desirable (or what I understood) behavior. However, I've moved everything to pointers (since is what I am used to with Qt).
Header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QCheckBox>
#include <QLayoutItem>
#include <QFormLayout>
#include <QMainWindow>
#include <QLabel>
class MainWindow : public QMainWindow
{
Q_OBJECT
QCheckBox *checkA, *checkB;
QLabel *labelA, *labelB, *labelC;
QFormLayout * m_formLayout;
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
void clear();
public slots:
void populate();
};
#endif // MAINWINDOW_H
cpp file:
#include "mainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget * centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
m_formLayout = new QFormLayout(centralWidget);
checkA = new QCheckBox;
labelA = new QLabel("Show Check Box B:");
connect(checkA, &QCheckBox::stateChanged, this, &MainWindow::populate);
int row = 0;
m_formLayout->insertRow(row++, labelA, checkA);
checkB = new QCheckBox;
labelB = new QLabel("Check Box B:");
m_formLayout->insertRow(row++, labelB, checkB);
labelC = new QLabel("Bottom label");
m_formLayout->insertRow(row++,labelC);
populate();
}
MainWindow::~MainWindow() {
}
void MainWindow::clear()
{
if (!checkA->isChecked())
{
checkB->hide();
labelB->hide();
}
}
void MainWindow::populate()
{
clear();
if(checkA->isChecked())
{
checkB->show();
labelB->show();
}
}

Solution:
void clear()
{
QLayoutItem *child;
while ((child = m_formLayout->takeAt(0)) != 0)
{
QLayout * layout = child->layout();
QSpacerItem * spacer = child->spacerItem();
QWidget * widget = child->widget();
if(layout && !doesOwnObject(layout)) delete layout;
if(spacer && !doesOwnObject(spacer)) delete spacer;
if(widget)
{
if(doesOwnObject(widget)) widget->setParent(0L);
else delete widget;
}
}
}
setParent had to be called.

Related

QT Layout not adding properly

I'm currently learning Qt by making a sort of "RecipeBook" application. It has a function that currently should only add layouts from QMap to my main QLayout. Unfortunately, said error occurs:
It happens most of the times when adding the last layout, but it's not a rule. I experimented much with this code, so I'm sorry it's so messy. When I started RefillRecipeLayout function the statement was "while(i != endIterator), but it behaved the same. Below I'm sending you my code:
mainwindow.cpp
void MainWindow::on_recalculateButton_clicked()
{
currentRecipe.RefreshIngredientsInMap(deleteButtonToIngredientLayoutMap);
RefillRecipeLayout();
}
void MainWindow::DeleteLayout(QLayout* layout)
{
ClearLayout(layout);
delete layout;
}
void MainWindow::ClearLayout(QLayout* layout)
{
while (layout->count() != 0)
{
QLayoutItem* item = layout->takeAt(0);
delete item->widget();
delete item;
}
}
void MainWindow::RefreshRecipeLayout()
{
ClearLayout(recipeLayout);
}
void MainWindow::RefillRecipeLayout()
{
ClearLayout(recipeLayout);
int recipeCount= recipeLayout->count();
int iCount=0;
QMap<QPushButton*, QHBoxLayout*>::const_iterator i = deleteButtonToIngredientLayoutMap.constBegin();
QMap<QPushButton*, QHBoxLayout*>::const_iterator endIterator = deleteButtonToIngredientLayoutMap.constEnd();
int end = std::distance(i, endIterator);
while (iCount < end)
{
QLayout* layout = i.value();
int recipeCount = recipeLayout->count();
auto isEmpty=recipeLayout->isEmpty();
auto isEnabled = recipeLayout->isEnabled();
bool exists = recipeLayout;
recipeLayout->addLayout(layout);
i++;
iCount++;
}
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QComboBox>
#include <QSpinBox>
#include <QTextEdit>
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QScrollBar>
#include "recipe.h"
#include "ingredient.h"
#include <vector>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_addIngredient_clicked();
void on_ExitButton_clicked();
void on_recalculateButton_clicked();
private:
Ui::MainWindow *ui;
QMap<QPushButton*, QHBoxLayout*> deleteButtonToIngredientLayoutMap;
QComboBox* ingredientUnitComboBox=nullptr;
QSpinBox* ingredientAmountSpinBox=nullptr;
QTextEdit* ingredientNameText=nullptr;
QLabel* recipeAmountLabel=nullptr;
QLabel* recipeNameLabel=nullptr;
QComboBox* recipeUnitCBox=nullptr;
QComboBox* newRecipeUnitCBox=nullptr;
QScrollArea* scrollArea=nullptr;
QVBoxLayout* recipeLayout=nullptr;
QSpinBox* recalculateSpinBox=nullptr;
QComboBox* recalculateUnitCBox=nullptr;
void PrepareComboBox(QComboBox* comboBox);
void DeleteIngredient();
void DeleteLayout(QLayout* layout);
void ClearLayout(QLayout* layout);
void RefillRecipeLayout();
Unit GetUnitFromCBox(QComboBox* comboBox);
void RefreshRecipeLayout();
};
#endif // MAINWINDOW_H
RefreshIngredientsFunction from Recipe.cpp (works well as far as I know, I've checked with breakpoints)
void Recipe::RefreshIngredientsInMap(QMap<QPushButton*, QHBoxLayout*> OtherMap)
{
QMap<QPushButton*, QHBoxLayout*>::const_iterator i = OtherMap.constBegin();
QMap<QPushButton*, QHBoxLayout*>::const_iterator iteratorEnd = OtherMap.constEnd();
int count=0;
while (i != iteratorEnd)
{
QHBoxLayout* ingredientLayout=new QHBoxLayout();
QHBoxLayout* layout = i.value();
int end = std::distance(OtherMap.constBegin(), OtherMap.constEnd());
int currentItemIndex=0;
while(layout->count()>0)
{
QWidget* widget = layout->takeAt(0)->widget();
if (QSpinBox* spinBox = qobject_cast<QSpinBox*>(widget))
spinBox->setValue(13);
ingredientLayout->addWidget(widget);
currentItemIndex++;
}
delete layout;
OtherMap.insert(i.key(), ingredientLayout);
i++;
count++;
}
}
Apart from some code style issues (irrelevant debug lines, don't name iterator "i") in your
void Recipe::RefreshIngredientsInMap(QMap<QPushButton*, QHBoxLayout*> OtherMap)
you are taking the map by copy. Then you are trying to modify it's content by deleting some layouts and inserting other layouts.
delete layout;
OtherMap.insert(i.key(), ingredientLayout);
The delete operation destroys the object your map was pointing to, and then after RefreshIngredientsInMap returns, the original map is pointing to destroyed objects, because the insertions were made on a copy.

Customized widget in scroll area cannot be scrolled up or down after setting setWidgetResizable to true in qt

I have a problem when working with QScrollArea. Mainly, I want to add a customized widget into a scroll area to reach:
scroll widget if widget's size is larger than parent(scroll)
customized widget can automatically resize its size to fill all space of scroll if it is smaller than scroll
But I failed. Setting setWidgetResizable to true can resize widget but cannot scroll. Otherwise, can scroll widget not resize.
I'm using qt-5.15.x. Here is the minimal example, I can scroll it if I comment out line scroll->setWidgetResizable(true);:
customwidget.h
#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H
#include <QWidget>
#include <QSize>
#include <QResizeEvent>
class CustomWidget : public QWidget
{
Q_OBJECT
public:
explicit CustomWidget(QWidget *parent = nullptr);
~CustomWidget()=default;
protected:
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
QSize sizeHint() const override;
private:
QVector<QRectF> _rects;
QSize _size;
};
#endif // CUSTOMWIDGET_H
customwidget.cpp
#include "customwidget.h"
#include <QPainter>
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
_size = QSize(120, 200);
for(int i = 0; i < 10; i++)
{
QRectF rect(QPointF(10, 60*i+2),QSize(100, 50));
_rects.push_back(rect);
}
}
void CustomWidget::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
// background
painter.setBrush(QColor(130,130,130));
painter.drawRect(QRectF(QPointF(0,0), _size));
// items
for(int i = 0; i < 10; i++)
{
painter.setBrush(QColor(43,43,43));
painter.drawRect(_rects[i]);
painter.setPen(QColor(255,255,255));
painter.drawText(_rects[i], QString("ITEM %1").arg(QString::number(i)));
}
}
void CustomWidget::resizeEvent(QResizeEvent *e)
{
_size = e->size();
}
QSize CustomWidget::sizeHint() const
{
int rows = _rects.size();
int height = rows * 60+2;
height = std::max(height, parentWidget()->height());
int width = 120;
return QSize(width, height);
}
Add the mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
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;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QScrollArea>
#include "customwidget.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setGeometry(0,0,300,200);
QVBoxLayout *layout = new QVBoxLayout(centralWidget());
layout->setContentsMargins(0,0,0,0);
CustomWidget *cw = new CustomWidget();
QScrollArea *scroll = new QScrollArea(this);
scroll->setWidgetResizable(true);
scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll->setStyleSheet("QScrollArea{background:rgb(0,0,0);border:none;}");
scroll->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
scroll->setWidget(cw);
layout->addWidget(scroll);
}
MainWindow::~MainWindow()
{
delete ui;
}
A possible solution is to set the sizePolicy to Minimum:
CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
_size = QSize(120, 200);
for(int i = 0; i < 10; i++)
{
QRectF rect(QPointF(10, 60*i+2),QSize(100, 50));
_rects.push_back(rect);
}
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
}

Subclass of QPlainText does not expand to fill the layout

I do not understand why the CodeEditor example from the Qt website does not appear to work as expected. Every time I run the code it displays it like this, really small and not expanding to take up all the available space. Does anyone have any idea why? I have even tried to set a fixed size, sizepolicy and minimum sizing. Not sure what I am missing here.
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.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include "codeeditor.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// controls
auto *widget_main = new QWidget(this);
auto *lay_main = new QVBoxLayout(widget_main);
auto *label = new QLabel("Test");
auto *editor = new CodeEditor();
// layout
lay_main->addWidget(label);
lay_main->addWidget(editor);
lay_main->setContentsMargins(5, 5, 5, 5);
}
MainWindow::~MainWindow()
{
}
codeeditor.h:
#ifndef CODEEDITOR_H
#define CODEEDITOR_H
#include <QPlainTextEdit>
#include <QObject>
class QPaintEvent;
class QResizeEvent;
class QSize;
class QWidget;
class LineNumberArea;
// Main text editor
class CodeEditor : public QPlainTextEdit
{
Q_OBJECT
public:
CodeEditor(QWidget *parent = 0);
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine();
void updateLineNumberArea(const QRect &, int);
private:
QWidget *lineNumberArea;
};
// Line number gutter
class LineNumberArea : public QWidget
{
public:
LineNumberArea(CodeEditor *editor) : QWidget(editor) {
codeEditor = editor;
}
QSize sizeHint() const override {
return QSize(codeEditor->lineNumberAreaWidth(), 0);
}
protected:
void paintEvent(QPaintEvent *event) override {
codeEditor->lineNumberAreaPaintEvent(event);
}
private:
CodeEditor *codeEditor;
};
#endif
codeeditor.cpp:
#include "codeeditor.h"
#include <QtWidgets>
#include <QFontMetrics>
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lineNumberArea = new LineNumberArea(this);
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
updateLineNumberAreaWidth(0);
highlightCurrentLine();
setFixedSize(200, 200);
setMinimumSize(200,200);
}
int CodeEditor::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
//int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
int space = 3 + 12 * digits;
return space;
}
void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
{
if (dy)
lineNumberArea->scroll(0, dy);
else
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
if (rect.contains(viewport()->rect()))
updateLineNumberAreaWidth(0);
}
void CodeEditor::resizeEvent(QResizeEvent *e)
{
QPlainTextEdit::resizeEvent(e);
QRect cr = contentsRect();
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
void CodeEditor::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> extraSelections;
if (!isReadOnly()) {
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
}
setExtraSelections(extraSelections);
}
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), Qt::lightGray);
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(Qt::black);
painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
}
QMainWindow is a special widget since it has a preset layout
So you must set the main widget through setCentralWidget():
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto *widget_main = new QWidget;
auto *lay_main = new QVBoxLayout(widget_main);
auto *label = new QLabel("Test");
auto *editor = new CodeEditor();
setCentralWidget(widget_main); // <-- +++
// layout
lay_main->addWidget(label);
lay_main->addWidget(editor);
lay_main->setContentsMargins(5, 5, 5, 5);
}
On the other hand if you are going to use layouts then you should not set a fixed size to the widget, in your case remove setFixedSize(200, 200) on the other hand it is recommended that you make the connections with the new syntax:
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lineNumberArea = new LineNumberArea(this);
connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateLineNumberArea);
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
updateLineNumberAreaWidth(0);
highlightCurrentLine();
// setFixedSize(200, 200); <-- ---
setMinimumSize(200,200);
}

Dynamic QLabel misplacing in UI

I'm trying to create a program that accepts images through drag and drop and shows those images on my UI, then I want to rename them and save them.
I got the drag and drop to work, but I have some issues with my Image placement and I can't seem to find where I'm making my mistake.
In the image you see my UI during runtime, in the top left you can see a part of the image I dragged into the green zone(this is my drag and drop zone that accepts images). The position I actually want it to be in should be the red square. The green zone is a Dynamic created object called Imagehandler that I created to handle the drag and drop of the images. The Red square is my own class that inherits from QLabel, I called it myiconclass. This class should hold the actual image data.
I think my mistake has to do with the layouts, but I can't see it.
Could I get some help with this please?
Imagehandler.h
#ifndef IMAGEHANDLER_H
#define IMAGEHANDLER_H
#include <QObject>
#include <QWidget>
#include <QLabel>
#include <QDrag>
#include <QDragEnterEvent>
#include <QMimeData>
#include <QList>
#include <QDebug>
//this class is designed to help me take in the images with drag and drop
class ImageHandler : public QWidget
{
Q_OBJECT
public:
explicit ImageHandler(QWidget *parent = nullptr);
QList<QImage> getImageListMemory() const;
void setImageListMemory(const QList<QImage> &value);
QList<QUrl> getUrlsMemory() const;
void setUrlsMemory(const QList<QUrl> &value);
private:
//QWidget Icon;
QLabel Icon;
QList <QImage> imageListMemory;
QList <QUrl> urlsMemory;
protected:
void dragEnterEvent(QDragEnterEvent * event);
void dragLeaveEvent(QDragLeaveEvent * event);
void dragMoveEvent(QDragMoveEvent * event);
void dropEvent(QDropEvent * event);
signals:
void transferImageSignal(QList <QImage>);
public slots:
};
#endif // IMAGEHANDLER_H
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QImageReader>
#include <QList>
#include <QWidget>
#include <QLabel>
#include <myiconclass.h>
#include <imagehandler.h>
#include <QGridLayout>
#include <QDebug>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QList<QImage> getImageListMemory() const;
void setImageListMemory(const QList<QImage> &value);
private:
Ui::MainWindow *ui;
QLabel Icon;
QList <QImage> imageListMemory;
QList <QUrl> urlsMemory;
QList<QWidget *> labelList;
ImageHandler * ImageHandlerMemory;
QGridLayout * grid2;
QList <MyIconClass *> memory;
signals:
public slots:
void setIconSlot(QList <QImage>);
};
#endif // MAINWINDOW_H
myiconclass.h
#ifndef MYICONCLASS_H
#define MYICONCLASS_H
#include <QWidget>
#include <QLabel>
//this class is based on a Qlabel and is only made so it can help me with the actual images, gives me more members if I need it
class MyIconClass : public QLabel
{
Q_OBJECT
public:
explicit MyIconClass(QWidget *parent = nullptr);
int getMyNumber() const;
void setMyNumber(int value);
private:
int myNumber;
signals:
public slots:
};
#endif // MYICONCLASS_H
imagehandler.cpp
#include "imagehandler.h"
ImageHandler::ImageHandler(QWidget *parent) : QWidget(parent)
{
setAcceptDrops(true);
}
QList<QImage> ImageHandler::getImageListMemory() const
{
return imageListMemory;
}
void ImageHandler::setImageListMemory(const QList<QImage> &value)
{
imageListMemory = value;
}
QList<QUrl> ImageHandler::getUrlsMemory() const
{
return urlsMemory;
}
void ImageHandler::setUrlsMemory(const QList<QUrl> &value)
{
urlsMemory = value;
}
void ImageHandler::dragEnterEvent(QDragEnterEvent * event)
{
event->accept();
}
void ImageHandler::dragLeaveEvent(QDragLeaveEvent * event)
{
event->accept();
}
void ImageHandler::dragMoveEvent(QDragMoveEvent * event)
{
event->accept();
}
void ImageHandler::dropEvent(QDropEvent * event)
{
QList <QImage> imageList2;
QList <QUrl> urls;
QList <QUrl>::iterator i;
urls = event->mimeData()->urls();
//imageList.append(event->mimeData()->imageData());
foreach (const QUrl &url, event->mimeData()->urls())
{
QString fileName = url.toLocalFile();
qDebug() << "Dropped file:" << fileName;
qDebug()<<url.toString();
QImage img;
if(img.load(fileName))
{
imageList2.append(img);
}
}
emit transferImageSignal(imageList2);
this->setUrlsMemory(urls);
this->setImageListMemory(imageList2);
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ImageHandler * handler = new ImageHandler(this);
handler->show();
QGridLayout *grid = new QGridLayout;
grid->addWidget(handler, 0, 0);
ui->groupBoxIcon->setLayout(grid);
ImageHandlerMemory = handler;
//connect(handler,SIGNAL(handler->transferImageSignal(QList <QUrl>)),this,SLOT(setIconSlot(QList <QUrl>)));
connect(handler,SIGNAL(transferImageSignal(QList<QImage>)),this,SLOT(setIconSlot(QList<QImage>)));
}
MainWindow::~MainWindow()
{
delete ui;
}
QList<QImage> MainWindow::getImageListMemory() const
{
return imageListMemory;
}
void MainWindow::setImageListMemory(const QList<QImage> &value)
{
imageListMemory = value;
}
void MainWindow::setIconSlot(QList<QImage> images)
{
printf("succes!");
this->setImageListMemory(images); //save the images to memory
QGridLayout *grid = new QGridLayout; //create the grid layout I want my images to be in
// create counters to remember the row and column in the grid
int counterRow =0;
int counterColumn =0;
int counter3 =0;
int counterImages = 0;
//iterate over each image in the list
QList <QImage>::iterator x;
for(x = imageListMemory.begin(); x != imageListMemory.end(); x++)
{
MyIconClass * myLabel = new MyIconClass(this); //create an object of my own class (which is a Qlabel with an int member)
QPixmap pixmap(QPixmap::fromImage(*x)); //create a pixmap from the image in the iteration
myLabel->setPixmap(pixmap); //set the pixmap on my label object
myLabel->show();
memory.append(myLabel); //add it to the memory so I can recal it
counterImages++;
}
while(counter3 < images.count())
{
grid2->addWidget(memory.value(counter3), counterRow, counterColumn);
counterColumn++;
counter3++;
if(counterColumn >= 5)
{
counterRow++;
counterColumn =0;
}
}
if(ImageHandlerMemory->layout() == 0)
{
ImageHandlerMemory->setLayout(grid2);
}
}
myiconclass.cpp
#include "myiconclass.h"
MyIconClass::MyIconClass(QWidget *parent) : QLabel(parent)
{
}
int MyIconClass::getMyNumber() const
{
return myNumber;
}
void MyIconClass::setMyNumber(int value)
{
myNumber = value;
}
As Benjamin T said I had to change this:
MyIconClass * myLabel = new MyIconClass(this);
into this:
MyIconClass * myLabel = new MyIconClass(ImageHandlerMemory);
Thanks Benjamin!
PS, I also had to add this line in my mainwindow constructor:
grid2 = new QGridLayout;

Emit Signal Only When QCheckBox is Checked

I am creating a set of QCheckBox dynamically based on some user input like so:
QWidget *wid = new QWidget();
QVBoxLayout *layout = new QVBoxLayout();
for(int i=0; i<NumberModes; i++)
{
int k = Amplitudes(i,0);
int m = Amplitudes(i,1);
QString ks = QString::number(k);
QString ms = QString::number(m);
QString position = QString::number(i);
QString mode = "A"+ks+ms;
QCheckBox *check = new QCheckBox(mode);
connect(check, SIGNAL(toggled(bool)), &mapper, SLOT(map()));
connect(check, SIGNAL(toggled(bool)), &SelectModes, SLOT(map()));
mapper.setMapping(check,position);
SelectModes.setMapping(check,mode);
layout->addWidget(check);
updateGeometry();
}
wid->setLayout(layout);
ui->scrollArea->setWidget(wid);
The QSignalMapper are then connected to another class that performs some calculations:
connect(&SelectModes, SIGNAL(mapped(QString)), this, SIGNAL(CheckBoxClicked2(QString)));
connect(this, SIGNAL(CheckBoxClicked2(QString)), &Supress2, SLOT(ListenSelectedModes(QString)));
connect(&mapper, SIGNAL(mapped(QString)), this, SIGNAL(CheckBoxClicked(QString)));
connect(this, SIGNAL(CheckBoxClicked(QString)), &Suppress, SLOT(ListenSelectedModes(QString)));
What I need is that the classes only receive signals when the QCheckBox are checked; meaning if you check it once, and then un-check it no signal should be emitted, or received. Not sure what the best approach is.
Any ideas?
With C++11 it's doable simply and without QSignalMapper. Here's an working example.
#include <QWidget>
#include <QCheckBox>
#include <QVBoxLayout>
class QCheckBox;
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
signals:
void checkBoxChecked(QCheckBox *checkBox);
};
Widget::Widget(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout;
for (int i = 0; i < 10; ++i) {
QCheckBox *checkBox = new QCheckBox("CheckBox " + QString::number(i + 1));
connect(checkBox, &QCheckBox::toggled, [=](bool checked) {
if (checked)
emit checkBoxChecked(checkBox);
});
layout->addWidget(checkBox);
}
setLayout(layout);
}
The suggestions given by user2672165 are excellent!
If you want to monitor only the check event but not the uncheck event, one way would be to subclass the QCheckBox widget so that it emits a particular signal only when the checkbox is checked (e.g. checkBoxChecked)
Then you connect your signal mapper to the custom signal checkBoxChecked, instead of the standard toggle(bool) signal.
In this way the slot associated to the signal mapper is invoked only when the checkbox is checked and not when it is unchecked.
Here is a simple example
#include <QApplication>
#include <QtGui>
#include <QVBoxLayout>
#include <QSignalMapper>
#include <QCheckBox>
#include <QDebug>
class CheckableCheckBox : public QCheckBox {
Q_OBJECT
public:
CheckableCheckBox(const QString &text, QWidget *parent = 0)
: QCheckBox(text, parent)
{
connect(this, SIGNAL(toggled(bool)),
this, SLOT(verifyCheck(bool)));
}
signals:
void checkBoxChecked();
public slots:
void verifyCheck(bool checked) {
if (checked)
emit checkBoxChecked();
}
};
class Test : public QWidget {
Q_OBJECT
public:
Test(QWidget *parent = 0) : QWidget(parent) {
QSignalMapper *mapper = new QSignalMapper();
QVBoxLayout *layout = new QVBoxLayout();
for (int i = 0; i < 10; i++) {
QString mode = "A" + QString::number(i);
CheckableCheckBox *check = new CheckableCheckBox(mode);
connect(check, SIGNAL(checkBoxChecked()),
mapper, SLOT(map()));
mapper->setMapping(check, QString::number(i));
layout->addWidget(check);
setLayout(layout);
}
connect(mapper, SIGNAL(mapped(QString)),
this, SLOT(CheckBoxClicked(QString)));
}
public slots:
void CheckBoxClicked(const QString &mapping) {
qWarning() << "Checkbox:" << mapping << " is checked";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Test *wid = new Test();
wid->show();
return a.exec();
}
#include "main.moc"
Edit:
If you want to monitor a change in the check status and then notify to some other portions of the code the status of the checkbox (which is probably what you want) you can do something like this... You don't even need a QSignalMapper...
I have implemented a test method testMonitorCheckStatus to show what I mean. Note that you need typedef QList<bool> CheckBoxStatusList; (at least as far as I know) to use QList as an argument to slots and signals.
Edit #2:
The number of checkboxes is set at object creation
Hope this helps
#include <QApplication>
#include <QtGui>
#include <QVBoxLayout>
#include <QSignalMapper>
#include <QCheckBox>
#include <QList>
#include <QDebug>
typedef QList<bool> CheckBoxStatusList;
class Test : public QWidget {
Q_OBJECT
public:
Test(int totalCheckboxes, QWidget *parent = 0) : QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout();
for (int i = 0; i < totalCheckboxes; i++) {
QString mode = "A" + QString::number(i);
QCheckBox *checkBox = new QCheckBox(mode);
connect(checkBox, SIGNAL(toggled(bool)),
this, SLOT(monitorCheckStatus()));
m_checkBoxList.append(checkBox);
layout->addWidget(checkBox);
}
setLayout(layout);
connect(this, SIGNAL(checkBoxStatusChanged(CheckBoxStatusList)),
this, SLOT(testMonitorCheckStatus(CheckBoxStatusList)));
}
public slots:
void monitorCheckStatus() {
CheckBoxStatusList checkBoxStatus;
for (int i = 0; i < m_checkBoxList.count(); ++i)
checkBoxStatus.append(m_checkBoxList.at(i)->isChecked());
emit checkBoxStatusChanged(checkBoxStatus);
}
void testMonitorCheckStatus(const CheckBoxStatusList &checkBoxStatus) {
for (int i = 0; i < checkBoxStatus.count(); ++i)
qWarning() << "Checkbox:" << i << " is" << (checkBoxStatus.at(i) ? "checked" : "unchecked");
qWarning(" ");
}
signals:
void checkBoxStatusChanged(const CheckBoxStatusList &checkBoxStatus);
private:
QList<QCheckBox *> m_checkBoxList;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Test *wid = new Test(10);
wid->show();
return a.exec();
}
#include "main.moc"