有条件地更改连接到 QFileSystemModel 的 QListView 中文件的颜色
Conditionally change color of files in QListView connected to QFileSystemModel
我有 QListView
和 QFileSystemModel
。根据 QTreeView
中的选择,QListView
显示文件夹的内容。
现在我需要根据某些情况更改文件名的颜色。
最初的想法是遍历 QListView 中的项目,并根据条件是否满足为每个项目设置颜色。然而,这似乎是不可能的,因为 QFileSystemModel
的 setData()
方法只接受对 EditRole
的更改,忽略类似 [参见 this]
的内容
self.FileModel.setData(index, QtGui.QBrush(QtCore.Qt.red), role=QtCore.Qt.ForegroundRole)
这也被指出了here
后者的建议是子类化 QItemDelegate
以便为 QListView 中的项目着色。
因此,我将 QStyledItemDelegate
子类化并重新实现其 paint()
方法以在满足条件的情况下以绿色显示文件名 - 这工作正常。然而它现在看起来有点丑陋:文件图标丢失并且 "mouse_over" 效果不再起作用。
虽然这个子类化无论如何都是一个混乱的解决方法,但我的首要问题是
- 有没有办法根据条件为连接到
QFileSystemModel
的 QListView
中的项目着色?
现在假设情况可能并非如此并坚持 QItemDelegate 的子类化,
- 有没有办法恢复具有漂亮选择和图标的原始行为?
- 有谁知道 QListView 中的 QFileSystemModel 最初使用哪个 ItemDelegate 以及如何使用它?
- 是否可以获取其源代码并从那里复制绘画方法?
这是使用子类化并显示描述行为的最小代码。它使用 QLineEdit
可以在其中键入字符串,这样所有包含该字符串的文件都会以绿色突出显示。
import sys
from PyQt4 import QtGui, QtCore
class MyFileViewDelegate(QtGui.QStyledItemDelegate ):
def __init__(self, parent=None, *args, **kwargs):
QtGui.QItemDelegate.__init__(self, parent, *args)
self.condition = None
self.isMatch = False
self.brush_active = QtGui.QBrush(QtGui.QColor("#79b9ed"))
self.brush_active_matched = QtGui.QBrush(QtGui.QColor("#58cd1c"))
self.pen = QtGui.QPen(QtGui.QColor("#414141") )
self.pen_matched = QtGui.QPen(QtGui.QColor("#39c819") )
self.pen_active = QtGui.QPen(QtGui.QColor("#eef2fd") )
self.pen_active_matched = QtGui.QPen(QtGui.QColor("#e7fade") )
def paint(self, painter, option, index):
text = index.data(QtCore.Qt.DisplayRole)
self.matchText(text)
painter.save()
######## set background
painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
if option.state & QtGui.QStyle.State_Selected:
if self.isMatch:
painter.setBrush(self.brush_active_matched)
else:
painter.setBrush(self.brush_active)
painter.drawRect(option.rect)
######## set font color
if option.state & QtGui.QStyle.State_Selected:
if self.isMatch:
painter.setPen(self.pen_active_matched)
else:
painter.setPen(self.pen_active)
else:
if self.isMatch:
painter.setPen(self.pen_matched)
else:
painter.setPen(self.pen)
painter.drawText(option.rect, QtCore.Qt.AlignLeft, text)
painter.restore()
def matchText(self, filename):
# testing condition. In the real case this is much more complicated
if (self.condition != None) and (self.condition != "") and (self.condition in filename):
self.isMatch = True
else:
self.isMatch = False
def setCondition(self, condition):
self.condition = condition
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None, useDelegate = False):
super(MainWidget, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
self.FolderModel = QtGui.QFileSystemModel()
self.FolderModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.AllDirs)
self.FolderModel.setRootPath("")
self.FolderView = QtGui.QTreeView(parent=self)
self.FolderView.setModel(self.FolderModel)
self.FolderView.setHeaderHidden(True)
self.FolderView.hideColumn(1)
self.FolderView.hideColumn(2)
self.FolderView.hideColumn(3)
self.FolderView.expanded.connect(self.FolderView.scrollTo)
self.FolderView.clicked[QtCore.QModelIndex].connect(self.browserClicked)
self.FileModel = QtGui.QFileSystemModel()
self.FileModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.FileView = QtGui.QListView(parent=self)
self.FileView.setModel(self.FileModel)
self.FileViewDelegate = None
if useDelegate:
self.FileViewDelegate = MyFileViewDelegate()
self.FileView.setItemDelegate(self.FileViewDelegate)
self.FileView.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection )
self.LineEdit = QtGui.QLineEdit()
self.LineEdit.textChanged.connect(self.changeCondition)
# Add Widgets to layout
self.layout().addWidget(self.FolderView)
self.layout().addWidget(self.FileView)
self.layout().addWidget(self.LineEdit)
def changeCondition(self, text):
if self.FileViewDelegate:
self.FileViewDelegate.setCondition(text)
def browserClicked(self, index):
# the signal passes the index of the clicked item
# set the FileView's root_index to the clicked index
dir_path = self.FileModel.filePath(index)
root_index = self.FileModel.setRootPath(dir_path)
self.FileView.setRootIndex(root_index)
class App(QtGui.QMainWindow):
def __init__(self, parent=None, useDelegate=False):
super(App, self).__init__(parent)
self.central = MainWidget(parent =self, useDelegate=useDelegate)
self.setCentralWidget(self.central)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App(None, True) # set False to view App without custom FileViewDelegate
thisapp.show()
sys.exit(app.exec_())
这是使用和不使用 QItemDelegate 子类的外观比较:
顺便提一下,这段代码的另一个问题是,一旦条件发生变化,就需要将鼠标移到QFileView中来启动重绘。我想知道我可以使用哪个插槽连接到 LineEdit.textChange
信号以直接执行此操作。
不需要项目委托。通过重新实现 QFileSystemModel
:
的 data
方法可以更简单地实现它
class FileSystemModel(QtGui.QFileSystemModel):
def __init__(self, *args, **kwargs):
super(FileSystemModel, self).__init__(*args, **kwargs)
self.condition = None
def setCondition(self, condition):
self.condition = condition
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def data(self, index, role=QtCore.Qt.DisplayRole):
if self.condition and role == QtCore.Qt.TextColorRole:
text = index.data(QtCore.Qt.DisplayRole)
if self.condition in text:
return QtGui.QColor("#58cd1c")
return super(FileSystemModel, self).data(index, role)
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None, useDelegate = False):
super(MainWidget, self).__init__(parent)
...
self.FileModel = FileSystemModel(self)
...
def changeCondition(self, text):
self.FileModel.setCondition(text)
我有 QListView
和 QFileSystemModel
。根据 QTreeView
中的选择,QListView
显示文件夹的内容。
现在我需要根据某些情况更改文件名的颜色。
最初的想法是遍历 QListView 中的项目,并根据条件是否满足为每个项目设置颜色。然而,这似乎是不可能的,因为 QFileSystemModel
的 setData()
方法只接受对 EditRole
的更改,忽略类似 [参见 this]
self.FileModel.setData(index, QtGui.QBrush(QtCore.Qt.red), role=QtCore.Qt.ForegroundRole)
这也被指出了here
后者的建议是子类化 QItemDelegate
以便为 QListView 中的项目着色。
因此,我将 QStyledItemDelegate
子类化并重新实现其 paint()
方法以在满足条件的情况下以绿色显示文件名 - 这工作正常。然而它现在看起来有点丑陋:文件图标丢失并且 "mouse_over" 效果不再起作用。
虽然这个子类化无论如何都是一个混乱的解决方法,但我的首要问题是
- 有没有办法根据条件为连接到
QFileSystemModel
的QListView
中的项目着色?
现在假设情况可能并非如此并坚持 QItemDelegate 的子类化,
- 有没有办法恢复具有漂亮选择和图标的原始行为?
- 有谁知道 QListView 中的 QFileSystemModel 最初使用哪个 ItemDelegate 以及如何使用它?
- 是否可以获取其源代码并从那里复制绘画方法?
这是使用子类化并显示描述行为的最小代码。它使用 QLineEdit
可以在其中键入字符串,这样所有包含该字符串的文件都会以绿色突出显示。
import sys
from PyQt4 import QtGui, QtCore
class MyFileViewDelegate(QtGui.QStyledItemDelegate ):
def __init__(self, parent=None, *args, **kwargs):
QtGui.QItemDelegate.__init__(self, parent, *args)
self.condition = None
self.isMatch = False
self.brush_active = QtGui.QBrush(QtGui.QColor("#79b9ed"))
self.brush_active_matched = QtGui.QBrush(QtGui.QColor("#58cd1c"))
self.pen = QtGui.QPen(QtGui.QColor("#414141") )
self.pen_matched = QtGui.QPen(QtGui.QColor("#39c819") )
self.pen_active = QtGui.QPen(QtGui.QColor("#eef2fd") )
self.pen_active_matched = QtGui.QPen(QtGui.QColor("#e7fade") )
def paint(self, painter, option, index):
text = index.data(QtCore.Qt.DisplayRole)
self.matchText(text)
painter.save()
######## set background
painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
if option.state & QtGui.QStyle.State_Selected:
if self.isMatch:
painter.setBrush(self.brush_active_matched)
else:
painter.setBrush(self.brush_active)
painter.drawRect(option.rect)
######## set font color
if option.state & QtGui.QStyle.State_Selected:
if self.isMatch:
painter.setPen(self.pen_active_matched)
else:
painter.setPen(self.pen_active)
else:
if self.isMatch:
painter.setPen(self.pen_matched)
else:
painter.setPen(self.pen)
painter.drawText(option.rect, QtCore.Qt.AlignLeft, text)
painter.restore()
def matchText(self, filename):
# testing condition. In the real case this is much more complicated
if (self.condition != None) and (self.condition != "") and (self.condition in filename):
self.isMatch = True
else:
self.isMatch = False
def setCondition(self, condition):
self.condition = condition
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None, useDelegate = False):
super(MainWidget, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
self.FolderModel = QtGui.QFileSystemModel()
self.FolderModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.AllDirs)
self.FolderModel.setRootPath("")
self.FolderView = QtGui.QTreeView(parent=self)
self.FolderView.setModel(self.FolderModel)
self.FolderView.setHeaderHidden(True)
self.FolderView.hideColumn(1)
self.FolderView.hideColumn(2)
self.FolderView.hideColumn(3)
self.FolderView.expanded.connect(self.FolderView.scrollTo)
self.FolderView.clicked[QtCore.QModelIndex].connect(self.browserClicked)
self.FileModel = QtGui.QFileSystemModel()
self.FileModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.FileView = QtGui.QListView(parent=self)
self.FileView.setModel(self.FileModel)
self.FileViewDelegate = None
if useDelegate:
self.FileViewDelegate = MyFileViewDelegate()
self.FileView.setItemDelegate(self.FileViewDelegate)
self.FileView.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection )
self.LineEdit = QtGui.QLineEdit()
self.LineEdit.textChanged.connect(self.changeCondition)
# Add Widgets to layout
self.layout().addWidget(self.FolderView)
self.layout().addWidget(self.FileView)
self.layout().addWidget(self.LineEdit)
def changeCondition(self, text):
if self.FileViewDelegate:
self.FileViewDelegate.setCondition(text)
def browserClicked(self, index):
# the signal passes the index of the clicked item
# set the FileView's root_index to the clicked index
dir_path = self.FileModel.filePath(index)
root_index = self.FileModel.setRootPath(dir_path)
self.FileView.setRootIndex(root_index)
class App(QtGui.QMainWindow):
def __init__(self, parent=None, useDelegate=False):
super(App, self).__init__(parent)
self.central = MainWidget(parent =self, useDelegate=useDelegate)
self.setCentralWidget(self.central)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App(None, True) # set False to view App without custom FileViewDelegate
thisapp.show()
sys.exit(app.exec_())
这是使用和不使用 QItemDelegate 子类的外观比较:
顺便提一下,这段代码的另一个问题是,一旦条件发生变化,就需要将鼠标移到QFileView中来启动重绘。我想知道我可以使用哪个插槽连接到 LineEdit.textChange
信号以直接执行此操作。
不需要项目委托。通过重新实现 QFileSystemModel
:
data
方法可以更简单地实现它
class FileSystemModel(QtGui.QFileSystemModel):
def __init__(self, *args, **kwargs):
super(FileSystemModel, self).__init__(*args, **kwargs)
self.condition = None
def setCondition(self, condition):
self.condition = condition
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def data(self, index, role=QtCore.Qt.DisplayRole):
if self.condition and role == QtCore.Qt.TextColorRole:
text = index.data(QtCore.Qt.DisplayRole)
if self.condition in text:
return QtGui.QColor("#58cd1c")
return super(FileSystemModel, self).data(index, role)
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None, useDelegate = False):
super(MainWidget, self).__init__(parent)
...
self.FileModel = FileSystemModel(self)
...
def changeCondition(self, text):
self.FileModel.setCondition(text)