我正在使用 python 在 Maya 中创建一个脚本,旨在导航本地目录文件夹。
我被要求使其导航的方式是使用一组列表,每个列表代表不同的目录级别。
现在选择第一个列表[类型],它会正确传播第二个列表
给我这个反馈 在列表 0 上单击的项目 已接收列:0,当前列索引:0 正确的栏目 不是最后一栏 选定的目录:D:\WORKSPACE ew_folder结构 D\ASSETS\PROPS 是目录
但是当我单击第二个列表上的某个项目时,它似乎根本没有响应。 在列表 1 上单击的项目 已接收列:0,当前列索引:1
我很困惑。
import os
import maya.cmds as cmds
from PySide2 import QtWidgets, QtGui
class AssetBrowser(QtWidgets.QWidget):
def __init__(self, asset_dir, parent=None):
super(AssetBrowser, self).__init__(parent)
self.asset_dir = asset_dir
self.levels = 6 # Number of levels to display
self.list_titles = ["Type", "Category", "Asset", "LOD", "User", "Scene"] # List titles
self.file_lists = [] # List to store file list widgets for each level
self.initUI()
def initUI(self):
self.setWindowTitle('Asset Browser')
layout = QtWidgets.QVBoxLayout(self)
# Adding the image
image_label = QtWidgets.QLabel()
pixmap = QtGui.QPixmap(r"D:\WORKSPACE\safeBank\images\safeBank_logoTitle_v001.png")
image_label.setPixmap(pixmap)
layout.addWidget(image_label)
# Add frame for project settings
project_frame = QtWidgets.QFrame()
project_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
project_layout = QtWidgets.QHBoxLayout(project_frame)
layout.addWidget(project_frame)
# Add button to set project directory
self.set_project_button = QtWidgets.QPushButton("Set Project")
self.set_project_button.clicked.connect(self.setProjectDirectory)
project_layout.addWidget(self.set_project_button)
# Add line edit for displaying selected folder path
self.folder_path_lineedit = QtWidgets.QLineEdit()
self.folder_path_lineedit.setReadOnly(True)
project_layout.addWidget(self.folder_path_lineedit)
# Create a layout for the lists
lists_layout = QtWidgets.QHBoxLayout()
layout.addLayout(lists_layout)
# Create frames for each list
for i in range(self.levels):
frame = QtWidgets.QFrame()
frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
frame_layout = QtWidgets.QVBoxLayout(frame)
lists_layout.addWidget(frame)
# Add title label
title_label = QtWidgets.QLabel(self.list_titles[i])
frame_layout.addWidget(title_label)
# Create the file list widget
file_list_widget = QtWidgets.QTreeWidget()
file_list_widget.setHeaderHidden(True)
frame_layout.addWidget(file_list_widget)
# Connect itemClicked signal to unique itemClicked method for each list
file_list_widget.itemClicked.connect(lambda item, col, index=i: self.itemClicked(item, col, index))
self.file_lists.append(file_list_widget)
# Add feedback field
feedback_field = QtWidgets.QLabel()
layout.addWidget(feedback_field)
self.feedback_field = feedback_field
# Set initial project directory
self.setProjectDirectory(self.asset_dir)
self.populateFileLists(self.asset_dir)
def populateFileLists(self, directory):
for level, file_list_widget in enumerate(self.file_lists):
file_list_widget.clear()
if os.path.exists(directory) and os.listdir(directory): # Check if directory exists and is not empty
self.populateDirectory(directory, file_list_widget)
directory = os.path.join(directory, os.listdir(directory)[0]) # Go down a level
else:
break # Stop populating lists if directory is empty or doesn't exist
def populateDirectory(self, directory, file_list_widget):
for item_name in sorted(os.listdir(directory)):
item_path = os.path.join(directory, item_name)
item = QtWidgets.QTreeWidgetItem(file_list_widget)
item.setText(0, item_name)
if os.path.isdir(item_path):
item.setIcon(0, QtGui.QIcon.fromTheme("folder"))
def itemClicked(self, item, column, index):
print(f"Item clicked on list {index}")
print(f"Received column: {column}, Current column index: {index}")
if column == index: # Only proceed if the click is in the correct column
selected_directory = self.asset_dir
# Build the path based on the selected folders in the previous columns
for level in range(index + 1):
selected_directory = os.path.join(selected_directory, self.file_lists[level].currentItem().text(0))
print(f"Selected directory: {selected_directory}")
if os.path.isdir(selected_directory):
# Update the path for the next column
self.populateFileList(selected_directory, self.file_lists[index + 1]) # Populate next column
self.feedback_field.setText(selected_directory) # Display selected directory in feedback field
else:
print(f"Selected directory is not valid: {selected_directory}")
def populateFileList(self, directory, file_list_widget):
file_list_widget.clear()
self.populateDirectory(directory, file_list_widget)
def setProjectDirectory(self, directory=None):
if directory is None:
directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Project Directory")
if directory:
self.asset_dir = directory
self.folder_path_lineedit.setText(directory)
cmds.workspace(directory, openWorkspace=True)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
asset_dir = r"D:\WORKSPACE\new_folderStructure\GF\3D\ASSETS"
asset_browser = AssetBrowser(asset_dir)
asset_browser.show()
sys.exit(app.exec_())
column
中的itemClicked()
参数是错误的,最重要的是因为它引用了单击的QTreeWidget的列,它与QTableView类似,可以显示多列。0
,这就是 if column == index:
只能在 first QTreeWidget 中工作的原因:该列始终为 0,第一个 QTreeWidget 具有 index
0 .
只需删除该比较即可解决问题:
def itemClicked(self, item, column, index):
selected_directory = self.asset_dir
for level in range(index + 1):
... etc.
请注意,为此目的使用 QTreeWidget 是完全没有意义的,因为您从未使用过 QTreeView 的主要功能,即显示层次结构。
由于您也从未使用多于一列,因此更准确的选择是使用 QListWidget。
class AssetBrowser(QWidget):
...
def initUI(self):
...
for i in range(self.levels):
...
file_list_widget = QListWidget()
frame_layout.addWidget(file_list_widget)
file_list_widget.itemClicked.connect(
lambda item, index=i: self.itemClicked(item, index))
self.file_lists.append(file_list_widget)
...
def populateDirectory(self, directory, file_list_widget):
for item_name in sorted(os.listdir(directory)):
item_path = os.path.join(directory, item_name)
item = QListWidgetItem(item_name, file_list_widget)
if os.path.isdir(item_path):
item.setIcon(QIcon.fromTheme("folder"))
def itemClicked(self, item, index):
selected_directory = self.asset_dir
for level in range(index + 1):
selected_directory = os.path.join(selected_directory,
self.file_lists[level].currentItem().text())
if os.path.isdir(selected_directory):
self.populateFileList(selected_directory,
self.file_lists[index + 1])
self.feedback_field.setText(selected_directory)
# you should always clear the remaining lists
for list_view in self.file_lists[index + 2:]:
list_view.clear()
还有一个重要的问题。默认情况下,如果给定路径超出“根”路径,
populateFileLists
会自动“填充”剩余视图;从用户体验的角度来看,这是错误的,因为它不会向用户显示其列表中当前选择的目录,并且您还按字母顺序对内容进行任意排序,这与 os.listdir(directory)[0]
和中使用的常规顺序不一致文件浏览器。
更合适的实现应该是:
def populateFileLists(self, directory):
prev_file_list = None
for level, file_list_widget in enumerate(self.file_lists):
file_list_widget.clear()
if os.path.isdir(directory):
self.populateDirectory(directory, file_list_widget)
if prev_file_list:
match = prev_file_list.findItems(
os.path.basename(directory), Qt.MatchExactly)
if match:
prev_file_list.setCurrentItem(match[0])
prev_file_list = file_list_widget
for entry in sorted(os.listdir(directory)):
entry = os.path.join(directory, entry)
if os.path.isdir(entry):
directory = entry
break
else:
break
else:
break
最后,在访问文件系统时,使用 QTreeWidget 或 QListWidget 等低级类会使事情变得有点麻烦并且容易出现错误/bug。
QFileSystemModel 通过文件系统提供了更合适的接口,应该考虑改为。
实现这一点需要进一步的努力,但也确保了更准确的文件系统和对象逻辑管理。我强烈建议遵循这条道路,如果您在尝试实现它时发现问题,请发布另一个问题。