I'm creating a script in Maya with python that is designed to navigate local directory folders.
The way I've been asked to make it navigate is with a collection of lists each representing a different directory level.

now on selecting the 1st list [Type] it propagates the 2nd list correctly
giving me this feedback Item clicked on list 0 Received column: 0, Current column index: 0 Correct column Not last column Selected directory: D:\WORKSPACE\new_folderStructure\3D\ASSETS\PROPS Is directory
but when i click on an item on the 2nd list it doesnt seem to respond at all. Item clicked on list 1 Received column: 0, Current column index: 1
I'm very confused.
- I have tried stripping it back to a more basic script that just adds numbers into each list when you click on an item in the list, this worked just fine.
- Re-introducing the dir navigation this errored again.
- I tried changing os.path.join to os.path.normpath()
- I tried introducing checks to tell me the dir selected and again it just tells me nothing on clicking the 2nd list.
- I tried changing the item Clicked Function, and separating it into new functions based on which list is interacted with.
- Even asked chat gtp for help and it just lost its mind.
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_())
The
columnargument initemClicked()is wrong, most importantly because it refers to the column of the clicked QTreeWidget, which, similarly to QTableView, can show multiple columns.You are only showing one column, so that value will always be
0, which is the reason for whichif column == index:only works in the first QTreeWidget: the column is always 0, and the first QTreeWidget hasindex0.Simply removing that comparison will solve the problem:
Note that using QTreeWidget for this purpose is completely pointless, because you are never using the primary feature of QTreeView, which is to show hierarchical structures.
Since you are also never using more than one column, a much more accurate choice is to use QListWidget.
There is another important issue. By default,
populateFileListsautomatically "fills" the remaining views if the given path goes beyond the "root" path; this is wrong from the UX perspective, because it doesn't show the user the currently selected directory within its list, and you're also arbitrarily sorting the contents alphabetically, which is not consistent withos.listdir(directory)[0]and with the conventional order used in file browsers.A more appropriate implementation should be the following:
Finally, when accessing the file system, using low level classes such as QTreeWidget or QListWidget makes things a bit cumbersome and prone to errors/bugs.
QFileSystemModel provides a more appropriate interface through the file system, and it should be considered instead.
Implementing this requires some further efforts, but also ensures a more accurate file system and object logic management. I strongly suggest to follow that path, then, and if you find issues while trying to achieve it, post another question.