How to, inside a QTreeView, delete a selected row? - c++

I tried to delete a selected row of a QTreeView with an event.
This is the function:
bool TargetEventFilter::deleteselect(bool delete)
{
auto select= examplemodel->selectionModel()->currentIndex();
auto parent= select.parent();
int row= select.row();
bool remove = examplemodel->model()->removeRow(row, parent);
return remove ;
}
So my problem is:
The event is working (tried with the debugger).The Parent and the indext is always right. But the problem is that the selected row will not be deleted. In "return remove" i get a "false".

why you do not used CurrentIndex ?
bool remove = examplemodel->model()->removeRow(row, select);

Related

Deleting QTreeView item using custom context menu crashes

I have my QTreeView with custom model and items. Each item can have context menu triggered by right click. My problem is that when I am trying to refresh the View after deleting an item using its context menu, the application crashes because it get segmentation fault. The context menu is run in other thread by exec() so it doesn't know when the item (and its context menu QMenu) is deleted and crashes.
void ProjectExplorerDock::onCustomContextMenu(const QPoint& point)
{
QModelIndex index = this->pProjectExplorerView->indexAt(point);
auto item = static_cast<TreeItem*>(index.internalPointer());
if(item)
{
if(item->getMenu())
{
item->getMenu()->exec(this->pProjectExplorerView->viewport()->mapToGlobal(point));
}
}
}
The triggered action emits a signal to delete the item. I've tried to do this after the exec() like this:
void ProjectExplorerDock::onCustomContextMenu(const QPoint& point)
{
QModelIndex index = this->pProjectExplorerView->indexAt(point);
auto item = static_cast<TreeItem*>(index.internalPointer());
if(item)
{
if(item->getMenu())
{
item->getMenu()->exec(this->pProjectExplorerView->viewport()->mapToGlobal(point));
emit refreshProject();
}
}
}
but it's much more work to distinguish if it is necessary to refresh or not.
Is there a way to stop the exec() when the QMenu is deleted?
Example function to show how it is done. It is called every time I want to refresh the view. Each TreeItem has QMenu.
void ProjectExplorerDock::fillModel(Project& project)
{
delete pTreeModel;
pTreeModel = new TreeModel(mHeaders);
if(!project.getName().empty())
{
QList<QVariant> projectName {QString::fromStdString(project.getName()), ""};
TreeItem* projectNameItem = new TreeItem(projectName);
pTreeModel->addNewTreeItem(projectNameItem, pTreeModel->getRootItem());
pProjectExplorerView->setModel(pTreeModel);
void TreeModel::addNewTreeItem(TreeItem* item, TreeItem* parentItem)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
item->setParent(parentItem);
parentItem->appendChild(item);
endInsertRows();
}
Okay, I found the answer.
Before deleting the QMenu make sure to call QMenu::hide() to stop the use of it.

QListWidget item at 0th index is selected by default

I have a QListWidget which has some item, and I have "Remove" button on my form which actually removes item from list. Problem is that when form load for first time and I press remove without selecting any item from list, it takes the item at 0th index by default and remove it. Following is the code segment:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->listWidget->addItem(new QListWidgetItem("Item1"));
ui->listWidget->addItem(new QListWidgetItem("Item2"));
ui->listWidget->addItem(new QListWidgetItem("Item3"));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnRemove_clicked()
{
printf("on_btnRemove_clicked() \n");
int currentRow = ui->listWidget->currentRow();
QModelIndex currentIndex = ui->listWidget->currentIndex();
QListWidgetItem* currentItem = ui->listWidget->currentItem();
printf("currentRow: %d\n", currentRow);
if(currentRow > -1) {
printf("currentIndex.data() %s: \n", currentIndex.data().toString().toStdString().c_str());
printf("currentItem->data(0): %s \n", currentItem->data(0).toString().toStdString().c_str());
QListWidgetItem* itemToDelete = ui->listWidget->takeItem(currentRow);
delete itemToDelete;
itemToDelete = NULL;
}
}
Any idea how to override this behavior or at least anyway that I can show that 0th index is selected with default blue background of item.
You can show the selected item after adding the items, by calling setCurrentItem:-
QListWidgetItem* pSelectedItem = new QListWidgetItem("Item1");
ui->listWidget->addItem(pSelectedItem);
ui->listWidget->addItem(new QListWidgetItem("Item2"));
ui->listWidget->addItem(new QListWidgetItem("Item3"));
ui->listWidget->setCurrentItem(pSelectedItem);
As the docs state: -
Unless the selection mode is NoSelection, the item is also be selected.
Alternatively, rather than retrieving the current Item, get the selected items with selectedItems(), which I would expect to return an empty list with which you can check the number of items: -
if(ui->listWidget->selectedItems().count())
{
// delete items
}
Note that a call to clearSelection states:-
Deselects all selected items. The current index will not be changed.
I would expect this means that requesting the current index or current item can return a valid index or item, which is why delete removes the item, even though it is not selected.
You can use the function setCurrentRow from QListWidget:
// Set the first row as current
ui->listWidget->setCurrentRow(0);
and add a blue background color with:
// Get default background color
QBrush defaultBrush = ui->listWidget->currentItem->background();
// Set background color
QBrush brush(Qt::blue);
ui->listWidget->currentItem->setBackground(brush);
to set the default color:
// Change background color with default color
ui->listWidget->currentItem->setBackground(defaultBrush);
Although it's a many-years-old question, recently I came across a similar problem and solved the problem by overriding the behavior mentioned by the op. To help people who are dealing with the same problem but are not satisfied with the accepted answer, I'll post my alternative solution here.
I found the weird behavior comes from QAbstractItemView::focusInEvent.
As shown below by the snippet from Qt source code, when a QListWidget (or whatever widget that inherits QAbstractItemView) receives a non-mouse focus event, its first item becomes its current item automatically if there isn't a current item.
// code.woboq.org/qt5/qtbase/src/widgets/itemviews/qabstractitemview.cpp.html#_ZN17QAbstractItemView12focusInEventEP11QFocusEvent
void QAbstractItemView::focusInEvent(QFocusEvent *event)
{
Q_D(QAbstractItemView);
QAbstractScrollArea::focusInEvent(event);
const QItemSelectionModel* model = selectionModel();
bool currentIndexValid = currentIndex().isValid();
if (model
&& !d->currentIndexSet
&& !currentIndexValid) {
bool autoScroll = d->autoScroll;
d->autoScroll = false;
QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index
if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason) {
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
currentIndexValid = true;
}
d->autoScroll = autoScroll;
}
if (model && currentIndexValid)
setAttribute(Qt::WA_InputMethodEnabled, (currentIndex().flags() & Qt::ItemIsEditable));
else if (!currentIndexValid)
setAttribute(Qt::WA_InputMethodEnabled, false);
d->viewport->update();
}
To override this behavior, make a subclass of QListWidget and override focusInEvent() along with focusOutEvent() in the subclass.
virtual void focusInEvent(QFocusEvent *event) override {
// bypass QAbstractItemView::focusInEvent
QAbstractScrollArea::focusInEvent(event);
}
virtual void focusOutEvent(QFocusEvent *event) override {
// bypass QAbstractItemView::focusOutEvent
QAbstractScrollArea::focusOutEvent(event);
}

Remove selected items from listWidget

How to remove selected items from a QListWidget?
I have tried write the following code, but it does not work.
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
foreach(QListWidgetItem item, items){
ui->listWidget->removeItemWidget(item);
}
One way to remove item from QListWidget is to use QListWidget::takeItem which removes and returns the item :
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
foreach(QListWidgetItem * item, items)
{
delete ui->listWidget->takeItem(ui->listWidget->row(item));
}
Another way is to qDeleteAll :
qDeleteAll(ui->listWidget->selectedItems());
To give a solution with removeItemWidget:
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
foreach(QListWidgetItem* item, items){
ui->listWidget->removeItemWidget(item);
delete item; // Qt documentation warnings you to destroy item to effectively remove it from QListWidget.
}

Hide the checkbox from a QListView item

I wave a QListView that is backed by a QStandardItemModel. Under certain circonstances, the QStandardItem are made checkable. A checkbox gets displayed besides the item's display. At some point, I want to remove hide the QStandardItem checkbox. I set its checkable state to false but it doesn't hide the checkbox (though it cannot be checked anymore).
The only way I have found of hiding the checkbox is to replace the item with a new one. This doesn't seem the proper way to preceed.
This is the code:
MyModel::MyModel(QObject *parent):QStandardItemModel(parent){}
void MyModel::createItem(int row, const QString &text)
{
setItem(row, new QStandardItem(text));
}
void MyModel::setCheckable(int row)
{
item(row)->setCheckState(Qt::Unchecked);
item(row)->setCheckable(true); // A checkbox appears besides the text
}
void MyModel::hideCheckBox(int row)
{
item(row)->setCheckState(Qt::Unchecked);
item(row)->setCheckable(false); // does not work
// I need to completely replace the item for the checkbox to disapear.
// This doesn't seem the proper way to proceed
setItem(row, new QStandardItem(item(row)->data(Qt::DisplayRole).toString()));
}
Is there better way to proceed?
When you call setCheckState or setCheckable, the qt will update the data of list item by adding or setting a Qt::CheckStateRole data. If the Qt::CheckStateRole data is existed, the check icon will be shown. So you need remove it from the data map of the list item.
Finally, the code of hideCheckBox should be:
void MyModel::hideCheckBox(int row)
{
// check the item pointer
QStandardItem* pitem = item(row);
if (pitem == NULL) return;
// find and delete the Qt::CheckStateRole data
QMap<int, QVariant> mdata = itemData(pitem->index());
if (mdata.remove(Qt::CheckStateRole))
{
setItemData(pitem->index(), mdata);
}
}
Hope it useful. :)
I think the presence of the check boxes in items defined by item flags, so that I would write the function in the following way:
void MyModel::hideCheckBox(int row)
{
// Does not set the Qt::ItemIsUserCheckable flag.
item(row)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}

How to remove all rows and child rows from QTreeview

I don't know why I have trouble removing all rows and sub rows from qtreeview
I'm using QStandardItemModel as the model. Now here is my code that doesn't work.
What could be the problem?
QModelIndex FirstQModelIndex;
QModelIndex parentQModelIndex;
int iMdlChidCound = m_model->hasChildren();
if(iMdlChidCound > 0)
{
// only if there at list 1 row in the view
FirstQModelIndex = m_model->item(0,0)->index();
QStandardItem* feedItem = m_model->itemFromIndex(FirstQModelIndex);
// get the parent of the first row its the header row
QStandardItem* parentItem = feedItem->parent();
// here im getting exception
int parent_rows= parentItem->hasChildren();
parentQModelIndex = m_model->indexFromItem(parentItem);
// now i like to delete all the rows under the header , and its dosnt work
if(parent_rows>0)
{
bool b = feedItem->model()->removeRows(0,y,parentQModelIndex);
}
}
It seems like a lot of what you're doing is superfluous. If your only goal is to remove all the rows from the model, you could probably just use QStandardItemModel::clear
In your code you're jumping between the model and the items in a way you don't have to.
if(m_model->hasChildren()) {
m_model->removeRows(0, m_model->rowCount());
}
That should do what you're seeking.
QStandardItemModel::clear()
Which clears all the items including the header rows.