Drag and drop within PyQt5 TreeView?

拥有回忆 提交于 2019-12-24 07:57:55

问题


I'm trying to implement a file directory using PyQt5. I'd like to incorporate drag and drop functionality within this tree to support both internal and external files (i.e. if I had some files on my desktop I'd like to be able to drop them into a folder in my PyQt view). This is what I have currently:

from PyQt5.QtWidgets import QTreeView,QFileSystemModel,QApplication, 
QMenu, QAbstractItemView
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from src import config

class Tree(QTreeView):
    def __init__(self):
        QTreeView.__init__(self)
        cfg = config.get()

        model = QFileSystemModel()
        model.setRootPath("/Users/")

        self.setModel(model)
        self.setRootIndex(model.index("/Users/"))
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.open_menu)

        self.setSelectionMode(self.SingleSelection)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)


    def open_menu(self):
        menu = QMenu()
        menu.addAction("Create new folder")
        menu.exec_(QCursor.pos())
if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    w = Main()
    w.show()
    sys.exit(app.exec_())

With the above code I can show a directory and its contents. I can drag an item but dropping it does nothing and no drop indication is seen. It's unclear how to

a. get drag and drop working within the view and

b. get it working with items outside of that context (like from the desktop for example).


回答1:


According to the docs:

readOnly : bool

This property holds whether the directory model allows writing to the file system

If this property is set to false, the directory model will allow renaming, copying and deleting of files and directories.

This property is true by default

so if you want to be able to move files you must set it to False:

model.setReadOnly(False)

You must overwrite the dragEnterEvent method and if there is a QUrl relative to a local resource you must accept it.

Then you have to overwrite the dropEvent method and if the event does not have source then it implies that it comes from an external source as a local file, then a logic is implemented to move files or directories checking if the file or directory exists or not creating the new one path.

class Tree(QTreeView):
    def __init__(self):
        QTreeView.__init__(self)
        model = QFileSystemModel()
        model.setRootPath(QDir.currentPath())

        self.setModel(model)
        self.setRootIndex(model.index(QDir.currentPath()))
        model.setReadOnly(False)

        self.setSelectionMode(self.SingleSelection)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)

    def dragEnterEvent(self, event):
        m = event.mimeData()
        if m.hasUrls():
            for url in m.urls():
                if url.isLocalFile():
                    event.accept()
                    return
        event.ignore()

    def dropEvent(self, event):
        if event.source():
            QTreeView.dropEvent(self, event)
        else:
            ix = self.indexAt(event.pos())
            if not self.model().isDir(ix):
                ix = ix.parent()
            pathDir = self.model().filePath(ix)
            m = event.mimeData()
            if m.hasUrls():
                urlLocals = [url for url in m.urls() if url.isLocalFile()]
                accepted = False
                for urlLocal in urlLocals:
                    path = urlLocal.toLocalFile()
                    info = QFileInfo(path)
                    n_path = QDir(pathDir).filePath(info.fileName())
                    o_path = info.absoluteFilePath()
                    if n_path == o_path:
                        continue
                    if info.isDir():
                        QDir().rename(o_path, n_path)
                    else:
                        qfile = QFile(o_path)
                        if QFile(n_path).exists():
                            n_path += "(copy)" 
                        qfile.rename(n_path)
                    accepted = True
                if accepted:
                    event.acceptProposedAction()



回答2:


In your tree view subclass you must implement dragEnterEvent dragMoveEvent and dropEvent.

class Tree(QTreeView):
    def __init__(self):
        QTreeView.__init__(self)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            # to get a list of files:
            drop_list = []
            for url in event.mimeData().urls():
                drop_list.append(str(url.toLocalFile()))
            # handle the list here
        else:
            event.ignore()


来源:https://stackoverflow.com/questions/48121711/drag-and-drop-within-pyqt5-treeview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!