Remove several rows in QTableView + QAbstractItemModel - python

I am having a trouble removing several rows on my QAbstractItemModel.
My problem is that if I select several rows, say row 1 and 3.
I then loop over my selected rows, and delete them. However after I deleted the row 1, then row 3 becomes the row 2, so I actually delete the row 4.
Here is my method to retrieve the selected rows :
def get_selected_rows(self):
view = self._view
selection = view.selectionModel()
return selection.selectedRows()
Here is my method to delete a specific row :
def delete_obj(self, row):
self._model.removeRow(row.row())
And here is my method in my controller :
def on_delete_clicked(self):
for selected_index in self._view.get_selected_rows():
self._view.delete_obj(selected_index)
Is there any way to avoid recalculating all the indexes after each iteration of my loop ?

I found a very simple solution, I just iterate on the reversed of my list, and it's fine :)
def on_delete_clicked(self):
for selected_index in reversed(self._view.get_selected_rows()):
self._view.delete_obj(selected_index)

Related

Python PyQt - deleting selected table rows, and deletes wrong rows if row isn't selected

So I have created a CSV database with PyQT5.The user Has the option to delete last row and selected row
So I have a problem with selecting and deleting selected row:
(I apologize if my problem is hard to understand I don't know how to explain it properly, so here is a small visual representation)
1.data1 1.data1
2.data2 -> user selects 2.data2 row and deletes it-> 2.data3
3.data3 3.data4
4.data4
-> but if the user decides to delete last row without selecting the row this is what happens
1.data1 1.data1
2.data3 2.data4
3.data4 -> user presses Delete row button, without selecting 3.data4 row ->
-> but the last row doesn't get deleted instead the index of previously selected row gets passed
and the last selected row gets deleted
I have managed to locate the problem somewhere in this block of code, My guess is the problem comes from the .currentIndex(), but I don't know how to set the currentIndex to be None or empty so that it doesn't carry a number.
def deleteSelectedRow(self):
self.chosenRow = self.table_Database.currentIndex().row()
print(self.chosenRow)
if self.chosenRow < 0:
return
else:
self.table_Database.removeRow(self.chosenRow)
I had tried setting it to a negative number but that caused the same effect the index of the selected row prevailed the same
The currentIndex() of an item view doesn't indicate a selected index: while normally using setCurrentIndex() also selects the index, you can have a current index even if none is selected.
When an index that was the current is removed from the view, Qt automatically sets (but does not select!) the new current to the previous row and/or column depending on what was removed, unless there is no available index (rowCount() or columnCount() return 0) or the deleted index was in the first row or column (in this case, it sets the first available row and/or column).
The simple solution is to check whether there's a selected index or not:
def deleteSelectedRow(self):
if self.table_Database.selectedIndexes():
row = self.table_Database.currentIndex().row()
else:
row = self.table_Database.rowCount() - 1
if row >= 0:
self.table_Database.removeRow(row)
Considering what explained above, you could also have extended selections which don't show what the "current index" is; if you're supporting selections upon more than one row, you should create a list of rows based on the selected indexes, and remove rows in reversed order:
def deleteSelectedRow(self):
selected = self.table_Database.selectedIndexes()
if selected:
# create a set of *unique* rows
rows = set(i.row() for i in selected)
else:
rows = [self.table_Database.rowCount() - 1]
for row in sorted(rows, reverse=True):
self.table_Database.removeRow(row)

How to delete specific rows in excel with openpyxl python if condition is met

Using openpyxl I am creating python script that will loop through the rows of data and find rows in which some of the column are empty - these will be deleted. The range of rows is 3 to 1800.
I am not excatly sure how to delete these row - please see code I have come up with so far.
What I was trying to achieve is to iterate through the rows and check if columns 4, 7 values are set to None. If True I wanted to return row number into suitable collection (need advise which one would be best for this) and then create another loop that would delete specific row number reversed as I don't want change the structure of the file by deleting rows with data.
I believe there may be easier function to do this but could not find this particular answer.
for i in worksheet.iter_rows(min_row=3, max_row=1800):
emp_id = i[4]
full_name = i[7]
if emp_id.value == None and full_name.value == None:
print(worksheet[i].rows)
rows_to_delete.append(i)
Your iteration looks good.
OpenPyXl offers worksheet.delete_rows(idx, amt=1) where idx is the index of the first row to delete (1-based) and amt is the amount of rows to delete beyond that index.
So worksheet.delete_rows(3, 4) will delete rows 3, 4, 5, and 6 and worksheet.delete_rows(2, 1) will just delete row 2.
In your case, you'd probably want to do something like worksheet.delete_rows(i, 1).

Access original index in QAbstractTableModel from selected row in QTableView

I am implementing a GUI for a python programme using Qt5 and PySide2. I have no problem understanding the C++ side of Qt, so feel free to point out Qt references not related with python.
I have some data that I display in a QTableView using a subclass of QAbstractTableModel. I also use a subclass of QSortFilterProxyModel to filter my table to only display a subset of the underlying data because it's a very large dataset. I offer the user the possibility to display only part of this data according to some criteria. This all works really well.
Then I have configured the QTableView so that the user can only select complete rows:
self.ui.candidatesTable.setSelectionBehavior(QTableView.SelectRows)
And in the object handling the UI I have implemented a slot that is called when the selection in the table changes:
#Slot(QItemSelection)
def handleSelectionChanged(self, item):
hasSelection = self.ui.candidatesTable.selectionModel().hasSelection()
if hasSelection:
selectedRows = self.ui.candidatesTable.selectionModel().selectedRows()
for row in selectedRows:
print(row.row())
My problem is that the value printed by print(row.row()) shows the row index in the currently displayed rows. If the user has selected filtering criteria that only displays 5 rows out of the several thousands, and then selects the first row, print(row.row()) will return 0 and not the original index in the underlying QAbstractTableModel.
My question is therefore the following: how can I access the original index in this situation?
You have to map the QModelIndex of the proxy model to the source model using the mapToSource() method:
#Slot(QItemSelection)
def handleSelectionChanged(self, item):
indexes = self.ui.candidatesTable.selectedIndexes()
proxy_model = self.ui.candidatesTable.model()
rows = set()
for index in indexes:
si = proxy_model.mapToSource(index)
rows.add(si.row())
for row in rows:
print(row)
Based on the previous answer by #eyllanesc, I implemented a solution using selectedRows() instead of selectedIndexes(). The latter returns indices for all selected columns and rows, while I was only interested in rows:
#Slot(QItemSelection)
def handleSelectionChanged(self, item):
hasSelection = self.ui.candidatesTable.selectionModel().hasSelection()
if hasSelection:
selectedRows = self.ui.candidatesTable.selectionModel().selectedRows()
for row in selectedRows:
proxy_model = self.ui.candidatesTable.model()
row_index = proxy_model.mapToSource(row).row()

PyQt4 - QTableView - How to loop over QTableView

I tried to find an example in python where I can loop over model elements of a QTableView and print the entire row.
I have found how to loop over selected rows but nothing when rows are not selected.
Can someone help me? No need to tell me how to create the model and how to apply it to the QTableModel. I am only interested how to iterate over the rows.
I think you confused model and view in some places within your question...
But, why not simply get the number of rows and columns and loop over all of them:
for irow in xrange(model.rowCount()):
row = []
for icol in xrange(model.columnCount()):
cell = model.data(model.createIndex(irow, icol))
row.append(cell)
# print all elems per row
print ', '.join(str(c) for c in row))
Things can probably be beautified and/or written in a more compact way, but that's basically all.
Or am I missing something?

Exclude given columns when sorting a table python

I am trying to sort a table but would like to exclude given columns by their names while sorting. In other words, the given columns should remain where they were before sorting. This is aimed at dealing with columns like "Don't know', "NA" etc.
The API I'm using is unique and company specific but it uses python.
A table in this API is an object which is a list of rows, where each row is a list of cells and each cell is a list of cell values.
I am currently have a working function which sorts a table but I would like to edit/modify this to exclude a given column by it's name but I am struggling to find a way.
FYI - "Matrix" can be thought of as the table itself.
def SortColumns(byRow=0, usingCellValue=0, descending=True):
"""
:param byRow: Use the values in this row to determine the sort order of the
columns.
:param usingCellValue: When there are multiple values within a cell use this
to control which value row within each cell is used for sorting
(zero-based)
:param descending: Determines the order in which the values should be
sorted.
"""
for A in range(0,Matrix.Count):
for B in range(0,Matrix.Count):
if(A==B):
continue; #do not compare rows against eachother
valA = Matrix[byRow][A][usingCellValue].NumericValue if Matrix[byRow][A].Count > usingCellValue else None;
valB = Matrix[byRow][B][usingCellValue].NumericValue if Matrix[byRow][B].Count > usingCellValue else None;
if(descending):
if valB < valA:
Matrix.SwitchColumns(A,B)
else:
if valA < valB:
Matrix.SwitchColumns(A,B)
I am thinking of adding a new parameter which takes a list of column names, and use this to bypass these columns.
Something like:
def SortColumns(fixedcolumns, byRow=0,usingCellValue=0,descending=True):
While iterating through the columns, You can use the continue statement to skip over columns that you don't want to move. Put these conditions at the start of your two loops:
for A in range(0,Matrix.Count):
a_name = ??? #somehow get the name of column A
if a_name in fixedcolumns: continue
for B in range(0,Matrix.Count):
b_name = ??? #somehow get the name of column B
if b_name in fixedcolumns: continue
if(A==B):
continue

Categories