QTableView row remove - c++

I created a table view like this:
I have a create button to create new rows and as you can see I defined a button for each row to delete that row by this code:
int i = 0;
QPushButton *viewButton;
QStandardItemModel *model;
void MainWindow::on_pushButton_clicked()
{
model->appendRow(new QStandardItem(QString("")));
viewButton = new QPushButton();
viewButton->setText("Delete " + QString::number(i));
ui->tableView->setIndexWidget(model->index(i , 7), viewButton);
connect(viewButton , SIGNAL(clicked()) , this , SLOT(button_clicked()));
i++;
}
and I created a slot for each button clicked for remove a row:
void MainWindow::button_clicked()
{
// by this line I can get the sender of signal
QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
}
as you can see I know witch button sends signal and now I need to delete that row.
here is my question:
how can I get the row of sender button in table view to remove that row?
I searched everywhere and I didn’t realise how to get the row and column of an item.

On workaround is to use QObject::setObjectName and set some names to the buttons you add :
viewButton.setObjectName(QString("%1").arg(i));
And in button_clicked slot you can retrieve the row number using the object name :
void MainWindow::button_clicked()
{
// by this line I can get the sender of signal
QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
int row = pb->objectName().toInt();
}
Note that you should update object names after a row is removed.
Another way is to use the QSignalMapper class which collects a set of parameterless signals, and re-emits them with integer, string or widget parameters corresponding to the object that sent the signal. So you can have one like:
QSignalMapper * mapper = new QSignalMapper(this);
QObject::connect(mapper,SIGNAL(mapped(int)),this,SLOT(button_clicked(int)));
When adding your buttons in each row of table view, you can connect the clicked() signal of the button to the map() slot of QSignalMapper and add a mapping using setMapping so that when clicked() is signaled from a button, the signal mapped(int) is emitted:
viewButton = new QPushButton();
viewButton->setText("Delete " + QString::number(i));
ui->tableView->setIndexWidget(model->index(i , 7), viewButton);
QObject::connect(viewButton, SIGNAL(clicked()),mapper,SLOT(map()));
mapper->setMapping(but, i);
This way whenever you click a button in a row, the mapped(int) signal of the mapper is emitted containing the row number and consequently button_clicked is called with a parameter containing the row number.
Also here you should update the mappings in button_clicked slot since the row is removed when you click an item.

Related

Qt table widget, button to delete row

I have a QTableWidget and for all rows I set a setCellWidget at one column to a button.
I would like to connect this button to a function that delets this row.
I tried this code, which does not work, because if I simply click my button I do not set the current row to the row of the button.
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
QPushButton *b = new QPushButton("delete",this);
ui->tableWidget->setCellWidget(ui->tableWidget->rowCount()-1,0,b);
connect(d,SIGNAL(clicked(bool)),this,SLOT(deleteThisLine()));
...
void MainWindow::deleteThisLine()
{
int row = ui->tableWidget->currentRow();
ui->tableWidget->removeRow(row);
}
How can I connect my button to a function in a way that the function knows which button (at which row) was pressed?
To remove the row we must first get the row, if we are inserting widgets inside the cells the currentRow() method will not return the appropriate row, in many cases it will return the row of the last cell without widget that has been selected.
For that reason you must opt for another solution, for this case we will use the indexAt() method of QTableWidget, but for this we need to know the position in pixels of the cell. when one adds a widget to a cell, this cell will be the parent of the widget, so we can access from the button to the cell using the parent() method, and then get the position of the cell with respect to the QTableWidget and use it in indexAt(). To access the button we will use the sender().
When the current cell is removed the focus is lost, a possible solution is to place the focus again in another cell.
void MainWindow::deleteThisLine()
{
//sender(): QPushButton
QWidget *w = qobject_cast<QWidget *>(sender()->parent());
if(w){
int row = ui->tableWidget->indexAt(w->pos()).row();
ui->tableWidget->removeRow(row);
ui->tableWidget->setCurrentCell(0, 0);
}
}
Use this connection way to connect signal to a slot:
connect(ui->btnDelete, &QPushButton::clicked, this,&MainWindow::deleteRow);
And delete for example a row on call function:
void MainWindow::deleteRow()
{
int row = ui->tableWidget->currentRow();
ui->tableWidget->removeRow(row);
}
Create a custom class, where you pass the created push button object and the row index. From your custom push button class, handle the push button press event and emit a custom signal (it will carry the index number) handled from the object where your custom pushbutton is created. Some related code are below, to give you a hint:
.h
class mypushbutton {
explicit mypushbutton(QObject *parent = 0, QPushButton *pushbutton = 0, int index = 0);
signal:
void deleteRow(int index);
}
.cpp
mypushbutton() {
connect(pushbutton, SIGNAL(clicked(bool)), this, SLOT(actionButtonClick(bool)));
}
actionbuttonclicked() { emit deleteRow(index);}

How to get the row number of widget placed in a cell of Qtablewidget when it get clicked?

What i'm trying is to get the row number of QcomboBox when user selects items. Although its easy to to get the cell column and row using
cellClicked(int,int)
signal, but it only works when there is no widget on the cell.
so how to get the row number in case if there is a widget placed in a cell.
Note: All the combobox are added dynamically
At-last i found 2 ways of doing it.
By setting the property of QComboBox
Using the QSignalMapper
First Method
QComboBox* mCombo = new QCombobox();
mComboBox->setProperty("row",(int) i); // i represents the row number in qtablewidget
In handler function where you are handling the clicked QComboBox
int row = sender()->property("row").toInt();
Second Method
QSignalMapper *signalMapper= new QSignalMapper(this); //Create a signal mapper instance
for (each row in table) {
QComboBox* mCombo = new QComboBox();
table->setCellWidget(row,col,combo);
connect(mCombo, SIGNAL(currentIndexChanged(int)), signalMapper, SLOT(map()));
/*connect each signal of QComboBox to signal Mapper slot (i.e map()) which in turns connected to the signal of signalMapper calling the SLOT associated with it (i.e rowFinder) */
signalMapper->setMapping(combo, (int)row); //assign mapping to each widgetusing set mapping
}
connect(signalMapper, SIGNAL(mapped(int)),
this, SLOT(rowFinder(int)));
function : rowFinder(int rowIndex)
int row = rowIndex; //here is the row indexof selected QComboBox

Accessing a the click() slot of a button generated during runtime - Qt Creator

I have a GUI project in Qt Creator that functions as a shopping list. I am using a QLineEdit to add items to a QTableWidget. The user types something in, presses the QPushButton. The slot then adds a new row to the QTableWidget with the input, in the first column, and a new QPushButton in the second column. I then want the user to be able to press the button and have it clear that row, but I don't know how to access that slot, or sender (I'm not sure the proper term.) Here is the code so far. itemList is my QTableWidget, itemInput is the QLineEdit.
void MainWindow::on_btnAddItem_clicked()
{
ui->itemList->insertRow(ui->itemList->rowCount());
ui->itemList->setItem((ui->itemList->rowCount())-1,0,new QTableWidgetItem(ui->itemInput->text()));
QPushButton *clear = new QPushButton("Clear",this);
ui->itemList->setIndexWidget(ui->itemList->model()->index(ui->itemList->rowCount()-1, 1), clear);
ui->itemInput->clear();
}
Here is when the program is initially run. Once they click the button, it runs on_btnAddItem_clicked()
Then it looks like this, and I want to make the clear button remove the row it is a part of.
Do I need to create a new slot? Any help?
You will need to make your own button class and inherit QPushButton. Something like this :
class MyButton : public QPushButton {
public:
MyButton();
QTableWidgetItem *titem;
}
And here you MainWindow :
void MainWindow::on_btnAddItem_clicked()
{
ui->itemList->insertRow(ui->itemList->rowCount());
ui->itemList->setItem((ui->itemList->rowCount())-1,0,new QTableWidgetItem(ui->itemInput->text()));
MyButton *clear = new MyButton("Clear",this);
clear->titem = ui->itemList->item(ui->itemList->rowCount()-1, 0);
connect(clear, SIGNAL(clicked()), SLOT(on_btnClear_Clicked()));
ui->itemList->setIndexWidget(ui->itemList->model()->index(ui->itemList->rowCount()-1, 1), clear);
ui->itemInput->clear();
}
void MainWindow::on_btnClear_Clicked()
{
MyButton *btn = (MyButton*)QObject::sender();
ui->itemList->removeRow(btn->titem->row());
}
Please note, it is only step to do it.

Removing item from QListWidget from inside a Widget

I have a QListWidget in my MainWindow that displays a list of VideoWidgets (a custom QWidget).
VideoWidget has a clickable label where on clicking the label it should delete a file and then remove the QListItem which holds the VideoWidget from the QListWidget. Here is my VideoWidget class:
VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent)
{
ClickableLabel *smallRed = new ClickableLabel(this)
//...
QObject::connect(smallRed,SIGNAL(clicked()),this,SLOT(removeVideo()));
}
void VideoWidget::removeVideo(){
//...code to remove a file
QListWidget* list = myParent->getList();
QListWidgetItem* item = list->takeItem(list->currentIndex().row());
myList->removeItemWidget(item);
}
The problem is that clicking the smallRed label will not select its item in the QListWidget which means that list->currentIndex().row() will return -1. Clicking anywhere else in the Widget does select the current item. For the code to work I currently have to first click anywhere in the VideoWidget and then click its ClickableLabel. Is there any way I can achieve the same effect with one single click on my ClickableLabel?
From your previous qestion, we suggested use signal and slots. For example:
for(int r=0;r<3;r++)
{
QListWidgetItem* lwi = new QListWidgetItem;
ui->listWidget->addItem(lwi);
QCheckBox *check = new QCheckBox(QString("checkBox%1").arg(r));
check->setObjectName("filepath");
connect(check,SIGNAL(clicked()),this,SLOT(echo()));
ui->listWidget->setItemWidget(lwi,check);
}
Slot:
void MainWindow::echo()
{
qDebug() << sender()->objectName() << "should be remmoved";
}
It is not unique way to solve this problem, but it shows all main things, with signals and slots mechanism, objectName and sender() you can achieve all what you need.
sender() return object which send signal, you can cast it, but if you need only objectName you should not cast.

QSignalMapper and original Sender()

I have a bunch of QComboBoxes in a table. So that I know which one was triggered I remap the signal to encode the table cell location (as described in Selecting QComboBox in QTableWidget)
(Why Qt doesn't just send the cell activated signal first so you can use the same current row/column mechanism as any other cell edit I don't know.)
But this removes all knowledge of the original sender widget. Calling QComboBox* combo = (QComboBox* )sender() in the slot fails, presumably because sender() is now the QSignalMapper.
I can use the encoded row/column to lookup the QComboBox in the table widget but that seems wrong. Is there a more correct way to do it?
e.g.:
// in table creator
_signalMapper = new QSignalMapper(this);
// for each cell
QComboBox* combo = new QComboBox();
connect(combo, SIGNAL(currentIndexChanged(int)), _signalMapper, SLOT(map()));
_signalMapper->setMapping(combo, row);
// and finally
connect(_signalMapper, SIGNAL(mapped(int)),this, SLOT(changedType(int)));
// slot
void myDlg::changedType(int row)
{
QComboBox* combo = (QComboBox* )sender(); // this doesn't work !!
}
EDIT: Added for future search: there is a new book "Advanced Qt Programming" by Mark Summerfield that explains how to do this sort of thing.
Why not connect the QComboBox's signal straight to your slot?
QComboBox *combo = ...
connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(changedType(int)));
And then in your slot you can use the sender() method to retrieve the QComboBox that was changed.
void myDlg::changedType(int row)
{
QComboBox *combo = qobject_cast<QComboBox *> sender();
if(combo != 0){
// rest of code
}
}
Alternatively, to use the QSignalMapper method you would just need to change your slot to use the mapping you set up:
void myDlg::changedType(int row)
{
QComboBox *combo = qobject_cast<QComboBox *>(_signalMapper->mapping(row));
if(combo != 0){
// rest of code
}
}
I don't know exact answer, but maybe you should use: QComboBox* combo = qobject_cast(sender()) instead of QComboBox* combo = (QComboBox* )sender(). Someting like this:
QObject* obj = sender();
QComboBox* combo = qobject_cast<QComboBox*>(obj);
if(combo)
{
doSomethingWithCombo(combo);
}
else
{
// obj is not QComboBox instance
}
But maybe QSignalMapper really substitutes itself instead of real sender...