在使用 PySide 的 Python 和 Qt 编写桌面应用程序时,需要显示一个树视图,其中的顶级项目是不同类型的对象,而不是它们的子项。具体来说,顶级项目是一个 Git 仓库,而它的子项是工作树中的目录。
对于仓库,我想显示它的路径和当前检出的分支。对于目录,我只想显示它的名称。
目前,我通过让我的 QAbstractItemModel 子类在底层模型对象(从 internalPointer() 方法检索)上使用 isinstance 来实现这一点,并决定如何格式化结果字符串。
我想知道有没有更 Pythonic (或只是不那么笨拙)的方法来执行这种双重分派。我不想为此目的在我的模型类中定义一个方法,用于 Git 仓库和工作树文件,因为我觉得这会违反 SRP。
欢迎任何想法或建议。另外,如果有谁能为这个问题想出一个不那么笨拙的标题,请告诉我。
2、解决方案
一种更 Pythonic 的方法是使用鸭子类型。鸭子类型是一种设计模式,它允许您在不知道对象的实际类型的情况下调用其方法。这可以通过使用 hasattr() 函数来完成,如下所示:
def get_view_info(model_object):
if hasattr(model_object, 'get_path'):
return model_object.get_path()
elif hasattr(model_object, 'get_name'):
return model_object.get_name()
else:
raise TypeError("Unknown model object type")
此函数将根据所提供模型对象(从 internalPointer() 方法检索)的类型,调用相应的 get_path 或 get_name 方法。
以下是一个代码示例,演示如何使用此函数:
from PySide2 import QtCore, QtGui, QtWidgets
class GitRepo(QtCore.QObject):
def __init__(self, path, branch):
super().__init__()
self.path = path
self.branch = branch
def get_path(self):
return self.path
def get_name(self):
return "Repository: {}".format(self.path)
class WorkTreeFile(QtCore.QObject):
def __init__(self, name):
super().__init__()
self.name = name
def get_name(self):
return self.name
class MyModel(QtCore.QAbstractItemModel):
def __init__(self, data):
super().__init__()
self.data = data
def rowCount(self, parent):
if parent.isValid():
return len(self.data[parent.row()])
else:
return len(self.data)
def columnCount(self, parent):
return 1
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
return get_view_info(self.data[index.row()])
def index(self, row, column, parent):
if parent.isValid():
return self.createIndex(row, column, parent.child(row, 0))
else:
return self.createIndex(row, column)
def parent(self, index):
if index.isValid():
return self.createIndex(index.row(), 0, index.parent())
else:
return QtCore.QModelIndex()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
repo = GitRepo("/path/to/repo", "master")
file1 = WorkTreeFile("file1")
file2 = WorkTreeFile("file2")
data = [repo, [file1, file2]]
model = MyModel(data)
view = QtWidgets.QTreeView()
view.setModel(model)
view.show()
app.exec_()
此代码创建一个 QAbstractItemModel 子类,它使用鸭子类型来确定如何格式化每个模型对象的视图信息。然后将此模型设置为 QTreeView,并显示树视图。
这种方法比使用 isinstance 更加 Pythonic,因为它不需要在模型类中定义任何特殊方法。它还更加灵活,因为您可以添加新的模型类型,而无需更改模型类。