Как перетаскивать элементы файлов внутри элемента папки, используя QFileSystemModel и QListView?

Я создаю виджет для просмотра и управления файлами внутри моего приложения qt. Чтобы построить это, я использую QFileSystemModel и QListView с режимом просмотра IconMode.

Он должен позволять перемещать файлы (элементы) в папки (другие элементы) с помощью QListView.

Мой вопрос в том, как мне это реализовать?

Во-первых, я пытаюсь переопределить функции supportedDragActions и supportedDropActions из ContentFileSystemModel, чтобы разрешить действия Move и Copy. Кроме того, я переопределяю функцию flags, чтобы включить перетаскивание. Наконец, я переопределяю canDropMimeData и dropMimeData, чтобы проверить, работают ли они, но похоже, что это не так.

Первая проблема заключается в том, что модель не позволяет поместить файл предмета в область QListView, если в курсоре отображается запрещенный значок (показан на изображении ниже).

Во-первых, я должен настроить модель так, чтобы предметы в папках могли сбрасываться. После этого я могу реализовать код для переноса перетаскиваемых элементов в папку.

Код готов воспроизвести проблему:

import sys
import os

from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *


class ContentFileSystemModel(QFileSystemModel):

    def __init__(self):
        super(ContentFileSystemModel, self).__init__()

    def supportedDragActions(self) -> Qt.DropActions:
        print("supportedDragActions")
        return Qt.MoveAction | super(ContentFileSystemModel, self).supportedDragActions() | Qt.CopyAction

    def supportedDropActions(self) -> Qt.DropActions:
        print("supportedDropActions")
        return Qt.MoveAction | super(ContentFileSystemModel, self).supportedDropActions() | Qt.CopyAction

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        defaultFlags = super(ContentFileSystemModel, self).flags(index)
        if not index.isValid():
            return defaultFlags
        fileInfo = self.fileInfo(index)
        # The target
        if fileInfo.isDir():
            # Allowed drop
            return Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled | defaultFlags
        # The source: should be directory( in that case)
        elif fileInfo.isFile():
            # Allowed drag
            return Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled | defaultFlags
        return defaultFlags

    def canDropMimeData(self, data: QMimeData, action: Qt.DropAction,
                        row: int, column: int, parent: QModelIndex) -> bool:
        print("canDropMimeData")
        return True

    def dropMimeData(self, data: QMimeData, action: Qt.DropAction,
                     row: int, column: int, parent: QModelIndex) -> bool:
        print("dropMimeData")
        return True


def main(argv):
    app = QApplication(sys.argv)

    path = "C:\\Users\\Me\\Desktop"
    file_system_model = ContentFileSystemModel()
    file_system_model.setRootPath(path)
    file_system_model.setReadOnly(False)

    lv_file_manager = QListView()
    lv_file_manager.setModel(file_system_model)
    lv_file_manager.setViewMode(QListView.IconMode)
    lv_file_manager.setRootIndex(file_system_model.index(path))
    lv_file_manager.setResizeMode(QListView.Adjust)

    lv_file_manager.setMovement(QListView.Static)
    lv_file_manager.setSelectionMode(QAbstractItemView.ExtendedSelection)
    lv_file_manager.setWrapping(True)
    lv_file_manager.setAcceptDrops(True)
    lv_file_manager.setDragEnabled(True)
    lv_file_manager.setDropIndicatorShown(True)
    lv_file_manager.setUniformItemSizes(True)
    lv_file_manager.setDragDropMode(QAbstractItemView.InternalMove)
    lv_file_manager.setFlow(QListView.LeftToRight)

    lv_file_manager.show()
    app.exec_()


if __name__ == "__main__":
    main(sys.argv)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
617
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы устанавливаете неправильное свойство движения , так как вы используете Статическое:

Элементы не могут быть перемещены пользователем.

При использовании IconMode этому свойству автоматически присваивается значение Free, поэтому вы можете просто удалить следующую строку:

lv_file_manager.setMovement(QListView.Static)

Другие важные реализации находятся в модели canDropMimeData() (которая должна возвращать True, если целью является доступный для записи каталог) и dropMimeData() (которая фактически перемещает файлы).

Последний шаг — переопределить dragMoveEvent(), чтобы предотвратить перемещение значков вокруг текущего представления.

Обратите внимание, что также были внесены следующие изменения:

  • flags() не должен возвращать ItemIsDragEnabled, если целью является файл;
  • setAcceptDrops(True) и setDragEnabled(True) не требуются, так как они автоматически устанавливаются, когда движение не Static (как в случае использования IconMode, как описано выше);
  • setDragDropMode() также не требуется;
class ContentFileSystemModel(QFileSystemModel):
    # ...
    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        defaultFlags = super(ContentFileSystemModel, self).flags(index)
        if not index.isValid():
            return defaultFlags
        fileInfo = self.fileInfo(index)
        if fileInfo.isDir():
            return Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled | defaultFlags
        elif fileInfo.isFile():
            # files should *not* be drop enabled
            return Qt.ItemIsDragEnabled | defaultFlags
        return defaultFlags

    def canDropMimeData(self, data: QMimeData, action: Qt.DropAction,
                        row: int, column: int, parent: QModelIndex) -> bool:
        if row < 0 and column < 0:
            target = self.fileInfo(parent)
        else:
            target = self.fileInfo(self.index(row, column, parent))
        return target.isDir() and target.isWritable()

    def dropMimeData(self, data: QMimeData, action: Qt.DropAction,
                     row: int, column: int, parent: QModelIndex) -> bool:
        if row < 0 and column < 0:
            targetDir = QDir(self.fileInfo(parent).absoluteFilePath())
        else:
            targetDir = QDir(self.fileInfo(self.index(row, column, parent)).absoluteFilePath())
        dataList = []
        # first check if the source is writable (so that we can move it) 
        # and that it doesn't already exist on the target path
        for url in data.text().splitlines():
            path = QUrl(url).toLocalFile()
            fileObject = QFile(path)
            if not fileObject.permissions() & QFile.WriteUser:
                return False
            targetPath = targetDir.absoluteFilePath(QFileInfo(path).fileName())
            if targetDir.exists(targetPath):
                return False
            dataList.append((fileObject, targetPath))
        # actually move the objects, you might want to add some feedback
        # if movement failed (eg, no space left) and eventually undo the
        # whole operation
        for fileObject, targetPath in dataList:
            if not fileObject.rename(targetPath):
                return False
        return True

class FileView(QListView):
    def dragMoveEvent(self, event):
        # accept drag movements only if the target supports drops
        if self.model().flags(self.indexAt(event.pos())) & Qt.ItemIsDropEnabled:
            super().dragMoveEvent(event)
        else:
            event.ignore()


def main(argv):
    # ...
    lv_file_manager = FileView()

Спасибо, музыкант. Когда я удаляю эту строку, элементы можно свободно перемещать в другие места в QListView. Однако, раз я хотел бы разрешить перемещение элементов только в папки, это нежелательная функция. В любом случае, это прогресс, когда модель позволяет сбрасывать предметы и вызывать функцию canDropMimeData. Попробую ограничить этот дроп только папками. Я буду сообщать здесь о любом прогрессе.

E.G. Cortes 27.12.2020 05:12

Если вы хотите получить больше контроля над целью перетаскивания, вы должны переопределить dropEvent в представлении. Некоторое время назад я самостоятельно написал небольшой файловый менеджер с Qt, поэтому, если бы вы могли лучше объяснить, как ваша программа должна вести себя в зависимости от ситуации, я могу дать вам несколько предложений и, возможно, отредактировать ответ.

musicamante 27.12.2020 05:26

Я хочу разрешить перетаскивание элементов (файлов и папок) только в другие элементы папки. Когда пользователь бросает его куда-то, кроме папки, ничего не должно происходить. Когда пользователь помещает его в папку, он должен быть перемещен туда.

E.G. Cortes 27.12.2020 06:48

Когда я говорю «перемещено», я имею в виду удалить файлы из текущей папки и поместить их в папку, в которую они были перенесены.

E.G. Cortes 27.12.2020 06:51

Другие вопросы по теме