用于浏览资源的 Maya UI 无法像列视图中的 OS X 查找器一样导航?

问题描述 投票:0回答:1

我正在设计一个资源浏览器,用于导航预定义的文件夹结构,在@musicamante的帮助下,它现在看起来好多了!

其功能与 OS X 的列视图模式下的查找器类似:

OS X's finder in Column view mode

我只是对列表的显示方式有点疑问。 启动时,列表似乎想显示列表中应为空白的根目录。

UI example with problem

有人知道我需要做什么才能让这些列表显示为空白吗? 或者通常让它们在列视图模式下的功能更像 OS X 的查找器? 目前我只能让它保持扩展至 6 个,但我不知道如果它们的目录结构超过 6 个文件夹深度该怎么办... 帮忙吗?

import os
from PySide2 import QtWidgets, QtGui, QtCore
import maya.cmds as cmds
import maya.mel as mel  # Import the Maya MEL module


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_views = []  # List to store file views for each level
        self.thumbnail_label = None
        self.file_info_text = None
        self.selected_file_path = None  # Variable to store the selected file path
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Asset Browser')
        layout = QtWidgets.QVBoxLayout(self)

        # Adding the image
        image_label = QtWidgets.QLabel()
        pixmap = QtGui.QPixmap(r"N:\Asset_Browser\tools\safeBank\icons\safeBank_logoTitle_v001.png")
        image_label.setPixmap(pixmap)
        layout.addWidget(image_label)

        # Add a frame for the project setting and folder path
        project_frame = QtWidgets.QFrame()
        project_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        project_frame_layout = QtWidgets.QHBoxLayout(project_frame)
        layout.addWidget(project_frame)

        # Add the set project button
        set_project_button = QtWidgets.QPushButton("Set Project")
        set_project_button.clicked.connect(self.setProject)
        project_frame_layout.addWidget(set_project_button)

        # Add the folder path line edit
        self.folder_path_lineedit = QtWidgets.QLineEdit()
        project_frame_layout.addWidget(self.folder_path_lineedit)

        # Add the refresh button
        refresh_icon = QtGui.QPixmap(r"N:\Asset_Browser\tools\safeBank\icons\refresh.png")
        refresh_label = QtWidgets.QLabel()
        refresh_label.setPixmap(refresh_icon)
        refresh_label.mousePressEvent = self.refreshDirectories
        project_frame_layout.addWidget(refresh_label)

        # Create a layout for the views, thumbnail, and file info
        views_thumbnail_layout = QtWidgets.QHBoxLayout()
        layout.addLayout(views_thumbnail_layout)

        # Create views for each level
        for i in range(self.levels):
            view = QtWidgets.QListView()
            model = QtWidgets.QFileSystemModel()
            model.setRootPath(QtCore.QDir.rootPath())
            view.setModel(model)
            view.setIconSize(QtCore.QSize(20, 20))  # Set icon size
            model.setFilter(QtCore.QDir.AllDirs | QtCore.QDir.Files | QtCore.QDir.NoDotAndDotDot)
            model.setNameFilters(["*.mb", "*.ma", "*.fbx"])
            model.setNameFilterDisables(False)
            view.clicked.connect(lambda index, i=i: self.viewClicked(index, i))
            views_thumbnail_layout.addWidget(view)
            self.file_views.append(view)
        
        # Populate only the first view upon launch
        self.populateDirectory(self.asset_dir, self.file_views[0])
        
        # Clear the root index for all other views to keep them blank
        for view in self.file_views[1:]:
            view.setRootIndex(QtCore.QModelIndex())
            
        # Connect the directory change for the first view to update subsequent views
        self.file_views[0].doubleClicked.connect(lambda index: self.populateNextView(index))
        
        # Add a frame for the thumbnail and file info
        info_frame = QtWidgets.QFrame()
        info_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        info_frame_layout = QtWidgets.QVBoxLayout(info_frame)
        views_thumbnail_layout.addWidget(info_frame)

        # Add thumbnail label
        self.thumbnail_label = QtWidgets.QLabel()
        self.thumbnail_label.setFixedSize(200, 200)
        info_frame_layout.addWidget(self.thumbnail_label)

        # Add file info text field
        self.file_info_text = QtWidgets.QTextEdit()
        info_frame_layout.addWidget(self.file_info_text)

        # Add feedback field
        self.feedback_field = QtWidgets.QLabel()
        layout.addWidget(self.feedback_field)

        # Add a frame for search and notes (occupying half the window width each)
        search_notes_frame = QtWidgets.QFrame()
        search_notes_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        search_notes_layout = QtWidgets.QHBoxLayout(search_notes_frame)
        layout.addWidget(search_notes_frame)

        # Add a frame for search (occupying half the window width)
        search_frame = QtWidgets.QFrame()
        search_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        search_frame_layout = QtWidgets.QVBoxLayout(search_frame)
        search_notes_layout.addWidget(search_frame)

        # Add the search bar to the search frame
        search_bar_layout = QtWidgets.QHBoxLayout()
        search_frame_layout.addLayout(search_bar_layout)
        self.search_bar = QtWidgets.QLineEdit()
        search_bar_layout.addWidget(self.search_bar)

        # Add the search button to the search frame
        search_button = QtWidgets.QPushButton("Search")
        search_button.clicked.connect(self.searchFiles)
        search_bar_layout.addWidget(search_button)
        
        # Connect returnPressed signal of the search bar to searchFiles method
        self.search_bar.returnPressed.connect(self.searchFiles)

        # Add the search results list to the search frame
        self.results_list = QtWidgets.QListWidget()
        search_frame_layout.addWidget(self.results_list)

        # Add a frame for notes (occupying half the window width)
        notes_frame = QtWidgets.QFrame()
        notes_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        notes_layout = QtWidgets.QVBoxLayout(notes_frame)
        search_notes_layout.addWidget(notes_frame)

        # Add notes box to the notes frame
        notes_label = QtWidgets.QLabel("Notes:")
        notes_layout.addWidget(notes_label)
        self.notes_box = QtWidgets.QTextEdit()
        notes_layout.addWidget(self.notes_box)

    def populateDirectory(self, directory, view):
        view.model().setRootPath(directory)
        view.setRootIndex(view.model().index(directory))
        
        # Find and select the current directory in the previous file list
        if len(self.file_views) > 1:
            prev_view = self.file_views[self.file_views.index(view) - 1]
            match_indexes = prev_view.model().match(
                prev_view.model().index(prev_view.model().rootPath()),
                QtCore.Qt.DisplayRole,
                os.path.basename(directory),
                1,
                QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive
            )
            if match_indexes:
                prev_view.selectionModel().select(
                    match_indexes[0], QtCore.QItemSelectionModel.ClearAndSelect)
    
            # Clear the root index for all other views to keep them blank
            for subsequent_view in self.file_views[self.file_views.index(view) + 1:]:
                subsequent_view.setRootIndex(QtCore.QModelIndex())


    def viewClicked(self, index, view_index):
        selected_file_path = self.file_views[view_index].model().filePath(index)
        self.folder_path_lineedit.setText(selected_file_path)  # Update folder path line edit
        if os.path.isdir(selected_file_path):
            self.populateDirectory(selected_file_path, self.file_views[view_index + 1])
        else:
            self.selected_file_path = selected_file_path
            self.displayNotes()
            self.displayThumbnail(selected_file_path)

    def displayNotes(self):
        # Check if a file is selected
        if self.selected_file_path is not None:
            # Construct the note file path by replacing the file extension with '.txt'
            note_file_path = os.path.splitext(self.selected_file_path)[0] + '.txt'
            # Check if the note file exists
            if os.path.isfile(note_file_path):
                # Read and display the contents of the note file
                with open(note_file_path, 'r') as f:
                    notes_content = f.read()
                self.notes_box.setText(notes_content)
                return
        # If no note file found or no file is selected, clear the notes box
        self.notes_box.clear()

    def displayThumbnail(self, file_path):
        # Construct the thumbnail file path
        thumbnail_path = os.path.splitext(file_path)[0] + '.png'
        # Debug print for the thumbnail path
        print(f"Thumbnail Path: {thumbnail_path}")
        # Check if thumbnail exists
        if os.path.exists(thumbnail_path):
            try:
                pixmap = QtGui.QPixmap(thumbnail_path)
                self.thumbnail_label.setPixmap(pixmap.scaled(200, 200, QtCore.Qt.KeepAspectRatio))
                return
            except Exception as e:
                print(f"Error loading thumbnail: {e}")
        else:
            print("Thumbnail not found.")

    def refreshDirectories(self, event):
        for view in self.file_views:
            view.model().refresh()

    def setProject(self):
        selected_directory = self.folder_path_lineedit.text()
        mel.eval('setProject "{}";'.format(selected_directory))

    def searchFiles(self):
        search_text = self.search_bar.text()
        selected_directory = self.folder_path_lineedit.text()
        results = []
    
        # Search only within the last selected folder
        for root, dirs, files in os.walk(selected_directory):
            for file in files:
                if search_text in file and file.lower().endswith(('.mb', '.ma', '.fbx')):
                    results.append(os.path.join(root, file))
    
        self.results_list.clear()
        self.results_list.addItems(results)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
    asset_dir = r"N:\Asset_Browser\work\Asset"
    asset_browser = AssetBrowser(asset_dir)
    asset_browser.show()
    sys.exit(app.exec_())
  • 发布时如何组织
  • 我尝试过改变它的刷新方式
  • 它如何响应点击

在需要之前似乎没有什么可以让它们保持空白。

非常感谢任何帮助!

python maya pyside2 qtwidgets qfilesystemmodel
1个回答
0
投票

在 Qt 项视图中,无效的 QModelIndex 表示模型的 root。对于 QFileSystemModel,这意味着文件系统根(*nix 上的

/
,Windows 上的“我的电脑”)。

没有直接的方法来显示具有此类模型的空列表(也许在 Windows 上使用正确的

QDir.Filters
,但我不确定),但有可能的替代方案:

  • 在程序路径中创建一个虚拟隐藏目录(确保它为空且没有写权限)并为其余视图设置其路径;
  • 创建一个空模型(例如 QStringListModel)并在每个剩余视图中使用它;
  • 子类 QFileSystemModel,并重写
    rowCount()
    ,以便在需要时始终返回 0;

为了实现第二种解决方案,您显然需要保留对模型的引用:只需在创建视图时创建一个属性,然后只需在需要时重置模型即可。

        self.dummy = QStringListModel()
        ...
        for i in range(self.levels):
            view = QtWidgets.QListView()
            view.fsModel = model = QtWidgets.QFileSystemModel()
            model.setRootPath(QtCore.QDir.rootPath())
            ...

    def populateDirectory(self, directory, view):
        view.setModel(view.fsModel)
        ...
        for subsequent_view in self.file_views[self.file_views.index(view) + 1:]:
            subsequent_view.setModel(self.dummy)

或者,您可以对模型进行子类化并提供一种使其“无效”的方法:

class MyFileSystemModel(QFileSystemModel):
    _valid = True
    def invalidate(self):
        self.modelAboutToBeReset.emit()
        self._valid = False
        self.modelReset.emit()

    def rowCount(self, parent=QModelIndex()):
        if self._valid:
            return super().rowCount(parent)
        return 0

    def setRootPath(self, path):
        self._valid = True
        return super().setRootPath(path)


class AssetBrowser(QtWidgets.QWidget):
    ...
    def populateDirectory(self, directory, view):
        view.model().setRootPath(directory)
        ...
        for subsequent_view in self.file_views[self.file_views.index(view) + 1:]:
            subsequent_view.invalidate()
© www.soinside.com 2019 - 2024. All rights reserved.