Nano Hash - криптовалюты, майнинг, программирование

Добавить дополнительную информацию к элементам в QTreeView/QFileSystemModel

Я хотел бы отображать каждый элемент в QTreeView по-разному в зависимости от количества атрибутов, хранящихся в базе данных, и в зависимости от того, является ли элемент папкой или файлом. Однако я не понимаю, как QTreeView или QFileSystemModel взаимодействуют с делегатом. Всякий раз, когда элемент должен быть отрисован, в том числе во время инициализации, я ожидаю предоставить делегату все необходимые параметры, а затем использовать серию операторов if внутри делегата, чтобы установить, как отрисовывается конкретный элемент. Я нашел только метод .setItemDelegate и не знаю, когда и как на самом деле вызывается делегат или как он перебирает все элементы в модели. Ниже приведен пример, основанный на материалах в Интернете. Есть две проблемы:

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

  2. Мне не удалось заставить этот подкласс QTreeView отображать значки папок и файлов.

Код:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class fileSystemDelegate(QItemDelegate):
    def __init__(self, parent=None):
        QItemDelegate.__init__(self, parent)        #shouldn't this insure the icons are drawn?

    def paint(self, painter, option, index):
        painter.save()

        # set background
        painter.setPen(QPen(Qt.NoPen))
        if option.state & QStyle.State_Selected:   #DURING DRAW LOOP: idx = self.currentIndex(); if self.fileSystemModel.isDir(idx): PAINT RED
            painter.setBrush(QBrush(Qt.red))
        else:
            painter.setBrush(QBrush(Qt.white))     #ELSE PAINT WHITE
        painter.drawRect(option.rect)

        # draw item
        painter.setPen(QPen(Qt.black))
        text = index.data(Qt.DisplayRole)
        painter.drawText(option.rect, Qt.AlignLeft, text)   #there is no painter.drawIcon?

        painter.restore()

class fileSystemBrowser(QTreeView):
    def __init__(self, parent=None):
        super().__init__(parent)

        delegate = fileSystemDelegate()
        self.setItemDelegate(delegate)                  # how to provide delegate with additional info about the item to be drawn ?

        self.fileSystemModel = QFileSystemModel()
        self.fileSystemModel.setRootPath(QDir.currentPath())
        self.setModel(self.fileSystemModel)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = fileSystemBrowser()
    window.show()
    sys.exit(app.exec_())

ИЗМЕНИТЬ 1:

Я добавил пример «базы данных» в виде словаря и изменил подход, чтобы полагаться на метод данных, а не на делегат. Я ожидаю, что этот код будет выполнять поиск по словарю всякий раз, когда информация отображается в дереве, и, следовательно, печатать на терминале, когда пользователь вводит C:\Program Files\Internet Explorer\ на компьютере с Microsoft Windows. Однако он просто отображает каталог, ничего не выводя на терминал. Я хотел бы знать:

  1. Как получить операторы if в методе данных для запуска каждого элемента на дисплее по мере их рисования?

  2. Как отобразить значок после отображения значка по умолчанию в той же строке?

Код:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

database = {'C:\Program Files\Internet Explorer\ExtExport.exe':(1,3), 'C:\Program Files\Internet Explorer\iexplore.exe':(0,0)}

class fileSystemBrowser(QTreeView):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.fileSystemModel = QFileSystemModel()
        self.fileSystemModel.setRootPath(QDir.currentPath())
        self.setModel(self.fileSystemModel)

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            path = self.fileSystemModel.filePath(index)
            if  self.fileSystemModel.isDir(index):
                if database.get(path) != None:
                    if database[path][0] > 0:
                        print("Acting on custom data 0.") # add another icon after the regular folder icon

                    if database[path][1] > 0:
                        print("Acting on custom data 1.") # add another (different) icon after the regular folder or previous icon

if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = fileSystemBrowser()
    window.show()
    sys.exit(app.exec_())

ИЗМЕНИТЬ 2:

Подкласс модели определенно имел значение. Теперь скрипт, похоже, вызывает мой новый метод данных для каждого элемента. К сожалению, метод данных пока не работает, поэтому в результате получается древовидное представление без значков и текста. Иногда я получаю сообщение об ошибке: «QFileSystemWatcher: не удалось добавить пути: C:/PerfLogs». Основываясь на примерах в Интернете, я прокомментировал, где, по моему мнению, могут быть мои ошибки, но я пока не могу заставить это работать. Что я делаю неправильно?

import sys
from PySide.QtCore import *
from PySide.QtGui import *

database = {'C:\Program Files\Internet Explorer\ExtExport.exe':(1,3), 'C:\Program Files\Internet Explorer\iexplore.exe':(0,0)}

class newFileModel(QFileSystemModel):

    def __init__(self, parent=None):
        QFileSystemModel.__init__(self, parent)
        #self.elements = [[Do I need this? What should go here?]]

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            path = self.filePath(index)
            if  self.isDir(index):
                if database.get(path) != None:
                    if database[path][0] > 0:
                        print("Acting on custom data 0.") # I can add code here for different color text, etc.

                    if database[path][1] > 0:
                        print("Acting on custom data 1.") # I'll add code later
        #return self.data(index, role) # Do I need this about here?


class fileSystemBrowser(QTreeView):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.fileSystemModel = newFileModel()
        self.fileSystemModel.setRootPath(QDir.currentPath())
        self.setModel(self.fileSystemModel)


if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = fileSystemBrowser()
    window.show()
    sys.exit(app.exec_())

  • В зависимости от того, что именно вы хотите сделать, делегат элемента может быть излишним. Вы уверены, что хотите нарисовать абсолютно все самостоятельно? Это может оказаться много работы. Какие конкретные свойства вы хотите изменить? Создание подкласса файловой модели и повторная реализация метода data выглядит гораздо проще в реализации. 19.10.2017
  • Я хотел бы изменить цвет фона, цвет текста, шрифт текста и, возможно, значки в представлении на основе параметров, которые (в конечном итоге) будут доступны для моего экземпляра класса fileSystemBrowser. Является ли это примером подкласса файловой модели и переопределения метода данных? Если да, то я, к сожалению, этого не понимаю. Я знаком с графическими системами типа ggplot2 для R. С делегатами Qt ищу и не нахожу ничего знакомого. 19.10.2017
  • Да, в этом ответе есть основа. Похоже, что все интересующие вас свойства могут быть изменены таким образом. Я могу предоставить демонстрацию pyside, если хотите. 19.10.2017
  • Я отредактировал свой вопрос, включив в него примеры данных и переопределение простого метода данных. Если индекс в методе данных является индексом в модели и обновляется по мере того, как каждый элемент рисуется на экране, то мой метод должен был записать в терминал. Это не так. Буду признателен за любые рекомендации, которые вы можете предложить. 20.10.2017
  • Вам нужно подклассифицировать модель, а не древовидную структуру. Я не вижу простого способа добавить несколько значков, кроме создания для них дополнительных столбцов. Идея вставлять дополнительные значки после значка файла кажется мне странной - это, безусловно, приведет к очень запутанному и уродливому интерфейсу. Дополнительные столбцы были бы гораздо более удобными для пользователя и намного проще в реализации. 20.10.2017
  • В ПОРЯДКЕ. Я позабочусь о разных значках позже, сейчас я просто хочу понять, как заставить работать новый метод данных. Я изменил свой код. Пожалуйста, смотрите РЕДАКТИРОВАТЬ 2. 22.10.2017
  • Вы изменили вопрос в каждом выпуске, что я не могу понять его сегодня, вы могли бы объяснить мне, что это такое, что вы хотите сегодня, чтобы быть в состоянии помочь вам. 22.10.2017
  • @eyllanesc, я все еще пытаюсь написать новый метод данных, который будет отображать каталоги и файлы по-разному в зависимости от записей в базе данных. Но для этого есть несколько шагов. Первый шаг, на мой взгляд, состоит в том, чтобы файловая модель отображалась нормально, но чтобы операторы печати срабатывали, когда я открываю каталог с файлами в базе данных. Таким образом, я знаю, что метод данных вызывается и имеет доступ к базе данных. Затем я изменю операторы печати, чтобы изменить способ отображения этих файлов. 22.10.2017
  • функция должна возвращать запрашиваемые данные, а в вашем случае она ничего не возвращает, если вы хотите знать только, вызывает ли она, я рекомендую return QFileSystemModel.data(self, index, role) в конце функции 22.10.2017
  • Что это (1, 0) и (0, 0) означает в базе данных? 22.10.2017
  • Ваше предложение позволяет нормально отображать дерево файлов. Спасибо. Однако, когда я открываю папку Internet Explorer, в которой расположены два файла в базе данных, операторы печати не запускаются. Я отредактирую этот вопрос, чтобы метод данных пытался установить параметры отображения. Я также прокомментирую базу данных. Представьте, что каждая запись файла в базе данных имеет ноль или более основных комментариев (первое число) и ноль или более второстепенных комментариев (второе число). Каждый файл (или папка) должен отображаться по-разному в зависимости от количества и типа комментариев. 22.10.2017
  • В Windows пути с пробелами часто вызывают проблемы, я бы рекомендовал распечатать переменный путь и убедиться, что он имеет тот же формат. 22.10.2017
  • В моем случае я пробовал это в Linux с путями без пробелов, и если он печатается, когда я ввожу пути, помещенные в базу данных 22.10.2017
  • Спасибо за проверку. Я пробовал все комбинации, которые обычно работают с путями к файлам в Windows, и пока ничего не получилось. Но я верю, что вы правы. 22.10.2017
  • @davideps. Я думаю, вам может понадобиться нормализовать пути к файлам, чтобы вы всегда сравнивали одну и ту же строку. Я добавил демонстрацию, которая должна решить эту проблему (однако проверена только на Linux). 22.10.2017
  • @ eyllanesc, с моим кодом было (как минимум) две проблемы. Путь должен быть записан как 'C:/Program Files/Internet Explorer/ExtExport.exe', а также он будет срабатывать только в том случае, если он находится в тесте для файлов, а не для каталогов: if not self.isDir(index): 22.10.2017

Ответы:


1

Вот базовая демонстрация, которая показывает, как добавить дополнительный столбец со значками и другим форматированием. Обратите внимание, что делается попытка нормализовать пути к файлам, чтобы сравнения и поиск в словаре были более надежными:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

database = {
    QFileInfo('C:\Program Files\Internet Explorer\ExtExport.exe').absoluteFilePath(): (1, 3),
    QFileInfo('C:\Program Files\Internet Explorer\iexplore.exe').absoluteFilePath(): (0, 0),
    }

class FileSystemModel(QFileSystemModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        style = qApp.style()
        self.icons = [
            style.standardIcon(QStyle.SP_MessageBoxInformation),
            style.standardIcon(QStyle.SP_MessageBoxWarning),
            ]

    def columnCount(self, parent=QModelIndex()):
        return super().columnCount(parent) + 1

    def data(self, index, role=Qt.DisplayRole):
        extra = False
        if index.isValid():
            extra = index.column() == self.columnCount(index.parent()) - 1
            info = self.fileInfo(index)
            path = info.absoluteFilePath()
            if path in database:
                major, minor = database[path]
                print('found:', (major, minor), path)
                if extra:
                    if role == Qt.DecorationRole:
                        if major > 0:
                            return self.icons[0]
                        else:
                            return self.icons[1]
                    elif role == Qt.DisplayRole:
                        return '%s/%s' % (major, minor)
                    elif role == Qt.ForegroundRole:
                        if minor > 2:
                            return QColor('red')
        if not extra:
            return super().data(index, role)

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if (orientation == Qt.Horizontal and
            role == Qt.DisplayRole and
            section == self.columnCount() - 1):
            return 'Extra'
        return super().headerData(section, orientation, role)

class FileSystemBrowser(QTreeView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.fileSystemModel = FileSystemModel()
        self.fileSystemModel.setRootPath(QDir.currentPath())
        self.setModel(self.fileSystemModel)
        self.header().moveSection(self.fileSystemModel.columnCount() - 1, 1)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = FileSystemBrowser()
    window.show()
    sys.exit(app.exec_())

ИЗМЕНИТЬ:

Все роли, используемые в методе data, задокументированы в перечислении ItemDataRole и вводятся следующим образом:

Каждый элемент в модели имеет набор связанных с ним элементов данных, каждый из которых имеет свою роль. Роли используются представлением, чтобы указать модели, какой тип данных ей нужен. Пользовательские модели должны возвращать данные в этих типах.

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

22.10.2017
  • это работает безупречно. Мне понадобится день или больше, чтобы изучить, как вы этого добились. Есть много новых (для меня) методов. 22.10.2017
  • @davideps. Рад, что ты нашел это полезным. Не стесняйтесь спрашивать, есть ли что-то, что вас озадачивает в методах, которые я использовал. 22.10.2017
  • Документ по QFileInfo ясен и выглядит очень полезным. Спасибо за знакомство. С помощью style = qApp.style() вы получаете копию объекта стиля приложения, а затем устанавливаете две иконки для адаптации к этому стилю? Запятая здесь SP_MessageBoxWarning), просто для того, чтобы скобка была на следующей строке? 23.10.2017
  • Из руководств по Python я научился всегда использовать заполнители с печатью и написал бы print('found:', (major, minor), path) как print('found: (%d,%d), %s' % (major, minor, path)). Я не понимал, что ваш синтаксис действителен. 23.10.2017
  • Ваш if path in database: гораздо более интуитивно понятен, чем мой if database.get(path) != None:. Я не знал, что in нацелится на ключи. 23.10.2017
  • Я не могу понять, почему вам нужно добавить единицу, а затем вычесть единицу из columnCount. Это проблема индексов, начинающихся с нуля или единицы? 23.10.2017
  • К сожалению, с if extra: по class FileSystemBrowser(QTreeView): я в полной растерянности. Вы тестируете разные роли, но где эти роли установлены? Является ли роль параметром элемента данных, фазой отрисовки на экране, чем-то еще? Не могли бы вы добавить несколько комментариев? 23.10.2017
  • (1) Я использовал встроенные значки из style(), чтобы пример работал без необходимости предоставления каких-либо файлов значков. Конечно, вы можете использовать любые значки, которые вам нравятся. Завершающая запятая по умолчанию — распространенная идиома в python: она упрощает сопровождение кода, потому что вы можете переставлять элементы в list, dict и т. д., не беспокоясь о последующем исправлении запятых. Без этого очень легко ввести тонкие ошибки. Например, в списке строк отсутствие запятой может привести к объединению соседних строк. 23.10.2017
  • (2) Для отладки print я обычно слишком ленив, чтобы печатать все эти форматы. (3) Метод get имеет свои применения, но in быстрее и читабельнее. (4) Да - количество всегда будет на единицу больше, чем самый высокий индекс, из-за подсчета с нуля. 23.10.2017
  • (5) Я добавил некоторые дополнительные пояснения к своему ответу. 23.10.2017
  • Я уже прочитал ItemDataRole и теперь сделал это снова. Есть еще что-то, чего я не понимаю. В этой строке вашего кода if role == Qt.DecorationRole: роль задается представлением, когда оно выводит элементы данных на экран? Он постоянно делает запросы от модели? Взгляд говорит: Эй! Я сейчас работаю над элементом данных с индексом 6, есть ли Qt.DecorationRole для этого элемента? Как насчет ToolTipRole? Хорошо, теперь элемент данных с индексом 7... 23.10.2017
  • @davideps. Вы можете думать о ролях как о стандартизированных ключах сопоставления. Это позволяет представлению запрашивать данные у модели, ничего не зная о ее реализации (и наоборот). Таким образом, роли являются частью спецификации интерфейса модели/представления — это договорные гарантии, которые позволяют компонентам взаимодействовать друг с другом, оставаясь при этом полностью независимыми. 23.10.2017
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..