Qt - Access a checkbox in a table - c++

I have a table and each row in the table has a checkbox in it's first column. I need to make it so I can detect which checkboxes are checked and delete those rows when a button is pressed.
QWidget * chkWidget = new QWidget();
QHBoxLayout *center = new QHBoxLayout();
center->setAlignment( Qt::AlignCenter );
center->addWidget( new QCheckBox );
chkWidget->setLayout( center );
ui->data_table->setCellWidget(rowCount,0, chkWidget);
Was this done right? If so how do I access the checkboxes at each row?

I talk about a QTableWidget. You can use a QList.You save your QCheckBox into this QList and use it, when there is some change
Maybe you should check out the documentation
QTableWidget: Link
QList: Link
Here is a solution. I cannot run it at the moment, so please tell me if it works. Please validate the row value. I am not sure if it's possible, that row can have the value -1 when you delete the last row ;)
#include "TestTableWidget.h"
#include "ui_TestTableWidget.h"
TestTableWidget::TestTableWidget(QWidget *parent) : QMainWindow(parent), ui(new Ui::TestTableWidget)
{
ui->setupUi(this);
tableWidget = new QTableWidget(this);
tableWidget->setColumnCount(1); // Just an example
ui->gridLayout->addWidget(tableWidget);
connect(tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotChange()));
for(int i = 1; i < 10; i++)
{
addRow("Row " + QString::number(i));
}
}
TestTableWidget::~TestTableWidget()
{
delete ui;
}
void TestTableWidget::addRow(QString text)
{
int row = tableWidget->rowCount();
qDebug() << "Current row count is " + QString::number(row);
// Add new one
QTableWidgetItem *item = new QTableWidgetItem(text);
tableWidget->insertRow(row);
tableWidget->setItem(row, 0, item);
// Add item to our list
}
void TestTableWidget::slotChange()
{
int row = tableWidget->currentRow();
qDebug() << "Change in table. Current row-index: " + QString::number(row);
// This value is zero-based, so you can use it in our list
}

Related

Automatically add/remove widgets in QGridLayout with fixed column width?

Say I'm creating an image gallery view that has 3 columns (i.e. it displays 3 images on each row). I can add new images to the end by using division and remainder:
int row = images.size() / 3;
int col = images.size() % 3;
gridLayout->addWidget(myImage, row, col);
The problem arises when I want to remove an image from the middle; it now leaves that middle row with just two images instead of "shifting" everything back and filling each row with 3 images (except the last row).
It seems like QGridLayout doesn't have too many features, am I missing something here or is the only option to implement everything myself? Or am I using the wrong tool (QGridLayout) in the first place?
AFAIK, there is no auto-relayout in QGridLayout. I'm even not sure whether QGridLayout is intended for this purpose.
IMHO, QTableView or QTableWidget might be the better choice.
(Concerning this, QAbstractItemModel::moveRows() comes into my mind.)
However, this doesn't mean that it cannot be achieved.
I made an MCVE to demonstrate this – testQDeleteFromLayoutShift.cc:
#include <cassert>
#include <vector>
#include <QtWidgets>
// comment out to get rid of console diagnostic output
#define DIAGNOSTICS
class PushButton: public QPushButton {
public:
PushButton(const QString &text, QWidget *pQParent = nullptr):
QPushButton(text)
{ }
#ifdef DIAGNOSTICS
virtual ~PushButton() { qDebug() << "Destroyed:" << this << text(); }
#else // (not) DIAGNOSTICS
virtual ~PushButton() = default;
#endif // DIAGNOSTICS
};
// number of columns in grid
const int wGrid = 3;
// fill grid with a certain amount of buttons
std::vector<QPushButton*> fillGrid(QGridLayout &qGrid)
{
const int hGrid = 5;
std::vector<QPushButton*> pQBtns; pQBtns.reserve(wGrid * hGrid);
unsigned id = 0;
for (int row = 0; row < hGrid; ++row) {
for (int col = 0; col < wGrid; ++col) {
QPushButton *pQBtn = new PushButton(QString("Widget %1").arg(++id));
qGrid.addWidget(pQBtn, row, col);
pQBtns.push_back(pQBtn);
}
}
#ifdef DIAGNOSTICS
qDebug() << "qGrid.parent().children().count():"
<< dynamic_cast<QWidget*>(qGrid.parent())->children().count();
qDebug() << "qGrid.count():"
<< qGrid.count();
#endif // DIAGNOSTICS
return pQBtns;
}
// delete a button from grid (shifting the following)
void deleteFromGrid(QGridLayout &qGrid, QPushButton *pQBtn)
{
qDebug() << "Delete button" << pQBtn->text();
// find index of widget in grid
int i = 0;
const int n = qGrid.count();
while (i < n && qGrid.itemAt(i)->widget() != pQBtn) ++i;
assert(i < n);
// find item position in grid
int row = -1, col = -1, rowSpan = 0, colSpan = 0;
qGrid.getItemPosition(i, &row, &col, &rowSpan, &colSpan);
// remove button from layout
QLayoutItem *pQItemBtn = qGrid.itemAt(i);
qGrid.removeItem(pQItemBtn);
// reposition all following button layouts
for (int j = i + 1; j < n; ++j) {
QLayoutItem *pQItem = qGrid.takeAt(i);
const int row = (j - 1) / wGrid, col = (j - 1) % wGrid;
qGrid.addItem(pQItem, row, col);
}
delete pQBtn;
#if 1 // diagnostics
qDebug() << "qGrid.parent().children().count():"
<< dynamic_cast<QWidget*>(qGrid.parent())->children().count();
qDebug() << "qGrid.count():"
<< qGrid.count();
#endif // 0
}
// application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
qWin.setWindowTitle(QString::fromUtf8("Demo Delete from QGridLayout (with shift)"));
QGridLayout qGrid;
qWin.setLayout(&qGrid);
std::vector<QPushButton*> pQBtns = fillGrid(qGrid);
qWin.show();
// install signal handlers
for (QPushButton *const pQBtn : pQBtns) {
QObject::connect(pQBtn, &QPushButton::clicked,
[&qGrid, pQBtn](bool) {
QTimer::singleShot(0, [&qGrid, pQBtn]() {
deleteFromGrid(qGrid, pQBtn);
});
});
}
// runtime loop
return app.exec();
}
A Qt project file to build this – testQDeleteFromLayoutShift.pro:
SOURCES = testQDeleteFromLayoutShift.cc
QT += widgets
Output in Windows 10 (built with VS2017):
Qt Version: 5.13.0
qGrid.parent().children().count(): 16
qGrid.count(): 15
After clicking on button “Widget 8”:
Delete button "Widget 8"
Destroyed: QPushButton(0x25521e3ef10) "Widget 8"
qGrid.parent().children().count(): 15
qGrid.count(): 14
After clicking on button “Widget 6”
Delete button "Widget 6"
Destroyed: QPushButton(0x25521e3f7d0) "Widget 6"
qGrid.parent().children().count(): 14
qGrid.count(): 13
After clicking on button “Widget 12”
Delete button "Widget 12"
Destroyed: QPushButton(0x25521e46ad0) "Widget 12"
qGrid.parent().children().count(): 13
qGrid.count(): 12
Notes:
I used lambdas for the signal handlers of QPushButton::clicked() to pass the related QGridLayout and QPushButton* to the handler deleteFromGrid() – IMHO the most convenient way.
deleteFromGrid() is called via a QTimer::singleShot() with delay 0. If I would call deleteFromGrid() in the signal handler of QPushButton::clicked() directly, this would result in some kind of Harakiri due to the last line in deleteFromGrid(): delete pQBtn;.
The nested lambdas might look a bit scaring, sorry.
While writing this answer I recalled an older of mine:
SO: qgridlayout add and remove sub layouts
which might be of interest.

Qt Implementation of selecting items from list into other list (Dual List, Accumulator, List builder, TwoListSelection ...)

I want to select an arbitrary amount of items from a list of arbitrary length.
A dropdown (QComboBox) doesn't allow checkable items. A list of checkable items would become clumsy with a lot of items.
I found this question in the User Experience SE subsite and this answer seems to be the solution that best suits my needs. It has many names, as a comment in said answer remarks: Dual List, Accumulator, List builder, TwoListSelection ...
The version from OpenFaces.org shown in the answer linked above:
I couldn't find an implementation in Qt, though. Should I implement it myself or is there an implementation available in Qt? Is there a recommended approach?
This widget does not come by default in Qt, but it is not complicated to build it, the first thing you should do is list the requirements:
The button with text >> is enabled if the list on the left is not empty, if pressed it must move all the items.
The button with text << similar to the previous one but with the list on the right
The button with text > is enabled if an item in the list on the left is selected, if pressed, the selected item should be moved to the right one.
The button with text < the same but for the right side.
The button with "up" text is enabled if an item is selected and this is not the first item in the list. when pressed you must move the current item to a higher position.
The button with "down" text is enabled if an item is selected and this is not the last item in the list. when pressed you must move the current item to a lower position.
As we see most of the logic depends on the selection of items, for this we connect the signal itemSelectionChanged with the slot that decides whether the buttons are enabled or not.
To move items we must use takeItem() and addItem(), the first removes the item and the second adds it.
Moving up or down is equivalent to removing the item and then inserting it, for this we use takeItem() again with insertItem()
All the above is implemented in the following widget:
#ifndef TWOLISTSELECTION_H
#define TWOLISTSELECTION_H
#include <QHBoxLayout>
#include <QListWidget>
#include <QPushButton>
#include <QWidget>
class TwoListSelection : public QWidget
{
Q_OBJECT
public:
explicit TwoListSelection(QWidget *parent = nullptr):QWidget{parent}{
init();
connections();
}
void addAvailableItems(const QStringList &items){
mInput->addItems(items);
}
QStringList seletedItems(){
QStringList selected;
for(int i=0; i<mOuput->count(); i++)
selected<< mOuput->item(i)->text();
return selected;
}
private:
void init(){
QHBoxLayout *layout = new QHBoxLayout(this);
mInput = new QListWidget;
mOuput = new QListWidget;
mButtonToSelected = new QPushButton(">>");
mBtnMoveToAvailable= new QPushButton(">");
mBtnMoveToSelected= new QPushButton("<");
mButtonToAvailable = new QPushButton("<<");
layout->addWidget(mInput);
QVBoxLayout *layoutm = new QVBoxLayout;
layoutm->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
layoutm->addWidget(mButtonToSelected);
layoutm->addWidget(mBtnMoveToAvailable);
layoutm->addWidget(mBtnMoveToSelected);
layoutm->addWidget(mButtonToAvailable);
layoutm->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
layout->addLayout(layoutm);
layout->addWidget(mOuput);
mBtnUp = new QPushButton("Up");
mBtnDown = new QPushButton("Down");
QVBoxLayout *layoutl = new QVBoxLayout;
layoutl->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
layoutl->addWidget(mBtnUp);
layoutl->addWidget(mBtnDown);
layoutl->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
layout->addLayout(layoutl);
setStatusButton();
}
void connections(){
connect(mOuput, &QListWidget::itemSelectionChanged, this, &TwoListSelection::setStatusButton);
connect(mInput, &QListWidget::itemSelectionChanged, this, &TwoListSelection::setStatusButton);
connect(mBtnMoveToAvailable, &QPushButton::clicked, [=](){
mOuput->addItem(mInput->takeItem(mInput->currentRow()));
});
connect(mBtnMoveToSelected, &QPushButton::clicked, [=](){
mInput->addItem(mOuput->takeItem(mOuput->currentRow()));
});
connect(mButtonToAvailable, &QPushButton::clicked, [=](){
while (mOuput->count()>0) {
mInput->addItem(mOuput->takeItem(0));
}
});
connect(mButtonToSelected, &QPushButton::clicked, [=](){
while (mInput->count()>0) {
mOuput->addItem(mInput->takeItem(0));
}
});
connect(mBtnUp, &QPushButton::clicked, [=](){
int row = mOuput->currentRow();
QListWidgetItem *currentItem = mOuput->takeItem(row);
mOuput->insertItem(row-1, currentItem);
mOuput->setCurrentRow(row-1);
});
connect(mBtnDown, &QPushButton::clicked, [=](){
int row = mOuput->currentRow();
QListWidgetItem *currentItem = mOuput->takeItem(row);
mOuput->insertItem(row+1, currentItem);
mOuput->setCurrentRow(row+1);
});
}
void setStatusButton(){
mBtnUp->setDisabled(mOuput->selectedItems().isEmpty() || mOuput->currentRow() == 0);
mBtnDown->setDisabled(mOuput->selectedItems().isEmpty() || mOuput->currentRow() == mOuput->count()-1);
mBtnMoveToAvailable->setDisabled(mInput->selectedItems().isEmpty());
mBtnMoveToSelected->setDisabled(mOuput->selectedItems().isEmpty());
}
QListWidget *mInput;
QListWidget *mOuput;
QPushButton *mButtonToAvailable;
QPushButton *mButtonToSelected;
QPushButton *mBtnMoveToAvailable;
QPushButton *mBtnMoveToSelected;
QPushButton *mBtnUp;
QPushButton *mBtnDown;
};
#endif // TWOLISTSELECTION_H
In the following link you will find a complete example.

qt when qtablewidget is sorting,how to add data in same row?

I use QTableWidget to insert data when it is sorting, there has a problem i can't. For example:
QtTest::QtTest(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.tableWidget->setSortingEnabled(true);
ui.tableWidget->sortByColumn(0, Qt::AscendingOrder);
}
void QtTest::on_pushButton_clicked()
{
static int i = 1;
ui.tableWidget->insertRow(0);
ui.tableWidget->setItem(0, 0, new QTableWidgetItem(tr("a%1").arg(i)));
ui.tableWidget->setItem(0, 1, new QTableWidgetItem(tr("b%1").arg(i+1)));
i++;
}
then click pushbutton, i can't add data to the row i want, Is there a elegant way to solve it?

QtableWidget does not show data

I have a class that creates random data which I would like to show in a tableview on the main window.
I added via Designer a table view to the main window and called it tblData.
I suspect the problem is related to this because when I call the constructor the ui file with some implementation is already there.
I have taken the "Detailed Description" section from http://qt-project.org/doc/qt-5/qtablewidget.html as guidance....
However, the table remains empty. I do not see why... Thank you very much.
include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStringList headers;
headers << "Datapoints";
Dataset *myData;
myData = new Dataset();
myData->createRandomData(10); // create a ten element vector
QVector<int> data;
data = myData->getDataVector(); // data vector created in class Dataset
qDebug() << data;
int i;
for (i = 0 ; i < data.size() ; i++){
QString datapoint;
datapoint = QString::number(data[i]);
QTableWidgetItem * newItem = new QTableWidgetItem(datapoint);
ui->tblData->setItem(i, 0, newItem); // works not
qDebug() << datapoint; // works
}
}
MainWindow::~MainWindow()
{
delete ui;
}
I think you have to define your table's dimensions before starting to populate it with the data, i.e.
ui->tblData->setRowCount(data.size());
ui->tblData->setColumnCount(1);
The reason is that by default the initial row and column count of the table is 0, so the newly added items are not visible.

How to add a string and an int to a QTableView when a button is clicked

How can I add a QSting and an int to a tableView at same time when a button is clicked?
What I want is to have the first column with names and the second column with numbers. Both items need to be added at the same row when a button is clicked.
Col 1--- Col 2---
First Name 1
Second Name 2
Third Name 3
Here is what I have which adds two strings, how can I convert the second cell to be an int?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
model = new QStandardItemModel();
model->setRowCount(0);
ui->tableView->setModel(model);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QStandardItem *userName = new QStandardItem(ui->lineEdit_Name->text());
QStandardItem *userNumber = new QStandardItem(ui->lineEdit_Number->text());
QList<QStandardItem*> row;
row <<userName << userNumber;
model->appendRow(row);
}
Thanks a lot
Does it really have to be an integer or can it just look like an integer?
Conventionally in tables, text is displayed left-aligned and numbers are displayed right-aligned. You can get this effect by setting the alignment for your second column:
QStandardItem *userNumber = new QStandardItem(ui->lineEdit_Number->text());
userNumber->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
If you need to use the data as an integer somewhere else in your program, you can convert the QString to an integer when you need it.
Ok, i will gather all in this answer:
First:
every data in a QStandardItemModel is a QVariant, so you can query the data either
to the model with
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const
to the QStandardItem with:
virtual QVariant data ( int role = Qt::UserRole + 1 ) const
Both will return a QVariant. You can get the int with
int toInt(bool * ok = 0) const
Note that the bool is optional, but will return true if the conversion was posible.
Also, you can check if it can be converted to an int:
bool QVariant::canConvert(int targetTypeId) const
yourVariant.canConvert( QMetaType::Int ) should return true.
To get what you want. I would use takeRow method from QStandardItemModel:
QList<QStandardItem *> takeRow(int row)
That its, the reverse to what you doing to append the row. So you know that you can ask the second element of the returned QList for the Int value.