Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QfileSystemModel : removes rows that gaps can exist within them



  • please see the following post !!! thanks



  • The same way you would reimplement any Python class' method:

    @
    class MyFileSystemModel(QFileSystemModel):
    def removeRows(self, row, count, parent=QModelIndex()):
    # Your implementation...
    @

    Not sure why you would want to reimplement this method on QFileSystemModel though...



  • I want to send files to recycle bin when removing rows ,
    since
    @bool QFileSystemModel::remove ( const QModelIndex & index ) const
    @

    deletes files from the file system; it does not move them to a location where they can be recovered.
    so I use another python module to do this
    https://pypi.python.org/pypi/Send2Trash
    since

    @ bool QAbstractItemModel::removeRows ( int row, int count, const QModelIndex & parent = QModelIndex() )@

    can only removes rows in one piece, next to each other without any gaps.
    but I want to removes rows that gaps can exist within them ,so I decided to reimplement this method with the following code ,but when removeRows in my code is run , the view then end up in an invalid state.I wonder how to fix this problem ……
    @
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    from send2trash import send2trash

    class FileSystemModel(QFileSystemModel):
    def init(self, parent=None):
    super(QFileSystemModel, self).init(parent)

    def removeRows(self, first, rows=1 , parent = QModelIndex()):
        file = self.index(first, 0, parent)
        filePath=self.filePath(file )
        print(filePath)
        self.beginRemoveRows(parent, first,  first+rows-1)
        try:
            send2trash(filePath)
        except  OSError as e:
            self.endRemoveRows()
            return False
        else:
            self.endRemoveRows()
            return True
    

    if name == "main":
    app = QApplication(sys.argv)
    model= FileSystemModel()
    model.setRootPath(r"E:\Project\viviDownloader\Download\finished")

    tableView=QTableView()
    tableView.setModel(model)
    displayedIndex=model.index(r"E:\Project\viviDownloader\Download\finished")
    tableView.setRootIndex(displayedIndex)
    
    tableView.setSelectionBehavior( QAbstractItemView.SelectRows );
    
    selectionModel = tableView.selectionModel()
    def on_removeRow_triggered():
        selectedRows=selectionModel.selectedRows()
        print(selectedRows)
        for row in range(len(selectedRows)):
            print(selectionModel.selectedRows()[0].row())
            model.removeRows(selectionModel.selectedRows()[0].row())
    
    def creatContextMenu():
        menu = QMenu(tableView)
        removeRows=QAction("removeRows",None, triggered=on_removeRow_triggered)
        menu.addAction(removeRows)
        menu.exec_(QCursor.pos())
    
    tableView.setContextMenuPolicy(Qt.CustomContextMenu)
    tableView.customContextMenuRequested.connect(creatContextMenu)
    tableView.show()
    
    sys.exit(app.exec_())
    

    @



  • It would be much easier just to reimplement remove() to use send2trash() and then call it for each index in your selection. The following example reimplements remove(), just right click on a file and click 'Remove' in the menu and it will end up in your trash/recycle bin:

    @
    import sip
    sip.setapi('QString',2)
    sip.setapi('QVariant',2)

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *

    from send2trash import send2trash

    class FSM(QFileSystemModel):
    def remove(self, index):
    if not index.isValid(): return False
    path=self.filePath(index)
    try:
    send2trash(path)
    return True
    except OSError: return False

    if name=="main":
    from sys import argv, exit

    class Widget(QWidget):
        def __init__(self, parent=None, **kwargs):
            QWidget.__init__(self, parent, **kwargs)
    
            l=QVBoxLayout(self)
    
            self._model=FSM(self)
            self._tree=QTreeView(
                self,
                contextMenuPolicy=Qt.CustomContextMenu,
                customContextMenuRequested=self.showMenu)
    
            self._tree.setModel(self._model)
            self._tree.setRootIndex(self._model.setRootPath('/'))
    
            l.addWidget(self._tree)
    
        @pyqtSlot(QPoint)
        def showMenu(self, pos):
            index=self._tree.indexAt(pos)
            if not index.isValid(): return
    
            menu=QMenu(self)
            menu.addAction(QAction("Remove", self, triggered=lambda: self._model.remove(index)))
            menu.popup(self._tree.viewport().mapToGlobal(pos))
    
    a=QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())
    

    @

    Hope this helps ;o)



  • yes ,it is easy to remove only one row ,but the user may want to remove many rows at the same time ,so I think to reimplement removing many rows will be a good design .I find another way to do this ,the following is my code
    @
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    from send2trash import send2trash

    if name == "main":
    app = QApplication(sys.argv)
    model= QFileSystemModel()
    model.setRootPath(r"E:\Project\viviDownloader\Download\finished")

    tableView=QTableView()
    tableView.setModel(model)
    displayedIndex=model.index(r"E:\Project\viviDownloader\Download\finished")
    tableView.setRootIndex(displayedIndex)
    
    tableView.setSelectionBehavior( QAbstractItemView.SelectRows );
    
    selectionModel = tableView.selectionModel()
    def on_removeRow_triggered():
        selectedRows=selectionModel.selectedRows()
        selectedFilePathList=list(map(model.filePath, selectedRows))
        print(selectedFilePathList)
    

    map(send2trash, selectedFilePathList)

        for filePath in selectedFilePathList:
            send2trash(filePath)
    
    def creatContextMenu():
        menu = QMenu(tableView)
        removeRows=QAction("removeRows",None, triggered=on_removeRow_triggered)
        menu.addAction(removeRows)
        menu.exec_(QCursor.pos())
    
    tableView.setContextMenuPolicy(Qt.CustomContextMenu)
    tableView.customContextMenuRequested.connect(creatContextMenu)
    tableView.show()
    
    sys.exit(app.exec_())
    

    @

    I wonder why

    map(send2trash, selectedFilePathList)

    doesn't work this time ?



  • My method would work just as well for multiple rows (sequential or not) as you would simply loop over the list of selected indexes in the same way:

    @
    ...
    map(model.remove, tableView.selectionModel().selectedRows())
    ...
    @

    Anyway, its hair splitting as both work equally well. As to your question, I suspect the reason your map() doesn't work is because filePath() returns a QString object and send2trash() expects a simple python string. You can either reimplement your on_removeRow_triggered() method as follows to cast QString to str:

    @
    def on_removeRow_triggered():
    selectedRows=selectionModel.selectedRows()
    map(lambda x: send2trash(str(model.filePath(x))), selectedRows)
    @

    Or you can set the SIP API to always use str instead of QString by placing the following at the top of the entry point to your program:

    @
    import sip
    sip.setapi('QString',2)
    @

    This will make any Qt method that returns QString return str instead.

    Hope this helps ;o)



  • I am using Python 3 ,I use type() to test the return type of filePath() ,and the result is that filePath() returns str rath2than QString


Log in to reply