[Maya Python] Crush the contents of the script 2 ~ list Notes

Purpose of this article

We will deepen your understanding while checking the contents of your own tools one by one, and use them for future tool development. I thoroughly understand that I've been through because I've been working with copy and paste until now. I'm writing a script in the same way and it works somehow, so I hope it helps people who end up with. This time, PyQt, QTreeView, and QStandardItemModel are the main ones.

Tool overview

Shows a list of Notes in the scene. Each node has an attribute called Notes. There is this feature at the bottom of the Attribute Editor, do you use it? Leaving a note here will help you understand what this node is doing. I often use it to leave notes for each node in my Maya file.

However, I couldn't find a way to list all Notes, so I made it a tool.

listNotes.gif It displays all the nodes in the scene where Notes are left, like a GIF image. You can select nodes and edit Notes.

** Operating environment: Maya2019.3, Maya2020.1 ** Since I am using Pyside2, it will not work before 2016.

Full code

# -*- coding: utf-8 -*-
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin
from PySide2 import QtWidgets, QtCore, QtGui
from maya import cmds

class Widget(QtWidgets.QWidget):
	def __init__(self):
		super(Widget, self).__init__()

		#Create grid layout
		layout = QtWidgets.QGridLayout(self)
		
		#Added tree view
		treeView = TreeView()
		layout.addWidget(treeView, 0, 0, 1, -1)
		
		#Item settings in tree view
		self.stockItemModel = StockItemModel()
		treeView.setModel(self.stockItemModel)

		#Select item setting in tree view
		self.itemSelectionModel = QtCore.QItemSelectionModel(self.stockItemModel)
		treeView.setSelectionModel(self.itemSelectionModel)

		#Select button
		button = QtWidgets.QPushButton('Select')
		button.clicked.connect(self.select)
		layout.addWidget(button, 1, 0)
		
		#Refresh button
		button = QtWidgets.QPushButton('Refresh')
		button.clicked.connect(self.refresh)
		layout.addWidget(button, 1, 1)
		
		#Initial loading
		self.refresh()
		
	#Select button operation
	def select(self):
		cmds.select(cl=True)
		indexes = self.itemSelectionModel.selectedIndexes()
		for index in indexes:
			(node, notes) = self.stockItemModel.rowData(index.row())			
			cmds.select(node, noExpand=True ,add=True)

	#Refresh button behavior
	def refresh(self):
		#Create an empty dictionary
		list = {}

		#Get the attribute with notes in the dictionary
		for node in cmds.ls(allPaths=True):
			if cmds.attributeQuery ('notes',node=node,exists=True):
				list[node] = cmds.getAttr(node+'.notes')
		
		#Empty the item model
		self.stockItemModel.removeRows(0, self.stockItemModel.rowCount())
		
		#Add an item from the retrieved dictionary
		for note in list:
			self.stockItemModel.appendItem(note, list[note])
			
					
class TreeView(QtWidgets.QTreeView):
	def __init__(self):
		super(TreeView, self).__init__()

		#Change the type of selection (like Excel)
		self.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection)
		#Alternately change the color of rows in the tree view
		self.setAlternatingRowColors(True)
	
		
class StockItemModel(QtGui.QStandardItemModel):
	def __init__(self):
		super(StockItemModel, self).__init__(0, 2)
		#Header settings
		self.setHeaderData(0, QtCore.Qt.Horizontal, 'Node Name')
		self.setHeaderData(1, QtCore.Qt.Horizontal, 'Notes')

	#Behavior when editing
	def setData(self, index, value, role=QtCore.Qt.EditRole):
		if role:
			#Get edited data
			(node, attr) = self.rowData(index.row())
			#Change Notes attributes
			cmds.setAttr(node+'.notes', value, type='string')
			#Change edited item
			self.item(index.row(), index.column()).setText(value)
			
			#Report success
			return True

	#Node from the specified line,Returns attr
	def rowData(self, row):
		#Get nodes and attributes
		node = self.item(row, 0).text()
		attr = self.item(row, 1).text()
		return (node, attr)

	#Add an item
	def appendItem(self, nodename, notes):
		#Create node non-editable
		nodeItem = QtGui.QStandardItem(nodename)
		nodeItem.setEditable(False)
		
		#Notes is editable and created
		notesItem = QtGui.QStandardItem(notes)
		notesItem.setEditable(True)

		#Add line
		self.appendRow([nodeItem, notesItem])	
	

class  MainWindow(MayaQWidgetBaseMixin, QtWidgets.QMainWindow):
	def __init__(self):
		super(MainWindow, self).__init__()

		self.setWindowTitle('List Notes')
		self.resize(430, 260)
		
		widget = Widget()
		self.setCentralWidget(widget)

def main():
	window = MainWindow()
	window.show()
	
if __name__ == "__main__":
	main()

Tool blueprint

listNotes.jpg

It looks like this. It is easy to understand if you draw a blueprint to understand the operation of the tool.

See details

See below for the previously written part. I will write about the new items. [Maya Python] Crushing the contents of the script 1 ~ Camera Speed Editor

Tree view appearance settings (QTreeView)

		#Added tree view
		treeView = TreeView()
		layout.addWidget(treeView, 0, 0, 1, -1)

Instance the TreeView () class and add it to the grid layout. By the way, the capital letters of the class name are PEP8 Recommended. For the sake of clarity, I distinguish between class names with uppercase letters and instance names with lowercase letters.

Let's jump a little, but first look at the TreeView class.

class TreeView(QtWidgets.QTreeView):
	def __init__(self):
		super(TreeView, self).__init__()

		#Change the type of selection (like Excel)
		self.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection)
		#Alternately change the color of rows in the tree view
		self.setAlternatingRowColors(True)

Inherit QtWidgets.QTreeView to TreeView. Basically, it is used as it is, so there are few overrides.

self.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection) Change the mode of selection. If you keep the default, you can only select one, so change it so that you can select multiple. ʻExtended Selection` allows you to make stepping stone selections like Excel.

Qt Selection Mode Note

self.setAlternatingRowColors(True) Alternately change the row color. These tree views become harder to see as the amount increases, so make them easier to see with a zebra pattern.

ListNotes_TreeView.jpg In this way, QTreeView sets the appearance and behavior of the tree view.

Set items in tree view (StockItemModel, QItemSelectionModel)

		#Item settings in tree view
		self.stockItemModel = StockItemModel()
		treeView.setModel(self.stockItemModel)

		#Select item setting in tree view
		self.itemSelectionModel = QtCore.QItemSelectionModel(self.stockItemModel)
		treeView.setSelectionModel(self.itemSelectionModel)

Instance the StockItemModel () class with the name self.stockItemModel Set the created item in the tree view created earlier.

Use QtCore.QItemSelectionModel and set it so that you can get the selection status of self.stockItemModel. There is no need to inherit and override it, so just instantiate it and use it. Set self.itemSelectionModel to treeView as the selection model with setSelectionModel.

Now let's look at the StockItemModel () class.

StockItemModel

Initialization, header setting (init)

class StockItemModel(QtGui.QStandardItemModel):
	def __init__(self):
		super(StockItemModel, self).__init__(0, 2)
		#Header settings
		self.setHeaderData(0, QtCore.Qt.Horizontal, 'Node Name')
		self.setHeaderData(1, QtCore.Qt.Horizontal, 'Notes')

Inherit QStandarItemModel as StockItemModel. Set the header by default.

super(StockItemModel, self).__init__(0, 2) At initialization, create a 0-row, 2-column item model. self.setHeaderData(0, QtCore.Qt.Horizontal, 'Node Name') Create a header in column 0 with the name Node Name. self.setHeaderData(1, QtCore.Qt.Horizontal, 'Notes') In the first column, create a header named Notes.

QtCore.Qt.Horizontal is the orientation setting, here the header, so specify the horizontal.

Set the operation when editing (setData)

	#Behavior when editing
	def setData(self, index, value, role=QtCore.Qt.EditRole):
		if role:
			#Get edited data
			(node, attr) = self.rowData(index.row())
			#Change Notes attributes
			cmds.setAttr(node+'.notes', value, type='string')
			#Change edited item
			self.item(index.row(), index.column()).setText(value)
			
			#Report success
			return True

Set how it behaves when edited. ʻIndex specifies the edited location, valuespecifies the edited content, androlespecifies the role. Specify the format suitable for editing withQtCore.Qt.EditRole`.

From the row of edited data, use the rowData class to get node, attr. rowData will be described later.

cmds.setAttr(node+'.notes', value, type='string') Maya command. The edited attribute is reflected in the node.

self.item(index.row(), index.column()).setText(value) The edited part is also reflected in the item in the tree view.

Get node and attribute from specified row (rowData)

This is the explanation of the rowData class that came out earlier.

	#Node from the specified line,Returns attr
	def rowData(self, row):
		#Get nodes and attributes
		node = self.item(row, 0).text()
		attr = self.item(row, 1).text()
		return (node, attr)

Returns the value as node, attr (node name, attribute content) from the specified row.

The ʻitemclass can get elements by (row, column). Since it is converted to object data as it is, it is converted to a character string with.text ()`.

Add an item (appendItem)

Create a function to add items. After that, call it from the button etc.

	#Add an item
	def appendItem(self, nodename, notes):
		#Create node non-editable
		nodeItem = QtGui.QStandardItem(nodename)
		nodeItem.setEditable(False)
		
		#Notes is editable and created
		notesItem = QtGui.QStandardItem(notes)
		notesItem.setEditable(True)

		#Add line
		self.appendRow([nodeItem, notesItem])

Add an item from the node name, Notes.

QtGui.QStandardItem (nodename) Sets the display content of the item. The nodeItem.setEditable (False) node is disabled. notesItem.setEditable (True) Make Notes editable.

self.appendRow([nodeItem, notesItem]) Add the settings to the row. ʻAppendRow is a function of QStandardItemModel`. One of the strengths of PySide is that you can use these functions as they are.

Creating a button

		#Select button
		button = QtWidgets.QPushButton('Select')
		button.clicked.connect(self.select)
		layout.addWidget(button, 1, 0)
		
		#Refresh button
		button = QtWidgets.QPushButton('Refresh')
		button.clicked.connect(self.refresh)
		layout.addWidget(button, 1, 1)

Basically, it is the same as [Maya Python] Crushing the contents of the script 1 ~ Camera Speed Editor. Next, let's take a look at the contents of the button.

Select button operation

	#Select button operation
	def select(self):
		cmds.select(cl=True)
		indexes = self.selItemModel.selectedIndexes()
		for index in indexes:
			(node, notes) = self.itemModel.rowData(index.row())			
			cmds.select(node, noExpand=True ,add=True)

cmds.select (cl = True) Removes the current selection state. selectedIndexes () is a function of QItemSelectionModel, which returns a list of selected indexes.

for index in indexes: Repeat for the selected number of times. self.itemModel.rowData (index.row ()) Get the node name and notes of the selected row. For details, refer to [Get node and attribute from specified row (rowData)](### Get node and attribute from specified row (rowData)). cmds.select (node, noExpand = True, add = True) Select the acquired node additionally. noExpand = True allows you to select a selection set rather than an object in the selection set. ʻAdd = True` Change to additional selection mode.

Refresh button behavior

	#Refresh button behavior
	def refresh(self):
		#Create an empty dictionary
		list = {}

		#Get the attribute with notes in the dictionary
		for node in cmds.ls(allPaths=True):
			if cmds.attributeQuery ('notes',node=node,exists=True):
				list[node] = cmds.getAttr(node+'.notes')
		
		#Empty the item model
		self.stockItemModel.removeRows(0, self.stockItemModel.rowCount())
		
		#Add an item from the retrieved dictionary
		for note in list:
			self.stockItemModel.appendItem(note, list[note])

for... Lists the nodes with notes in dictionary format.

self.stockItemModel.removeRows(0, self.stockItemModel.rowCount()) Delete from the 0th row to the number of rows (self.stockItemModel.rowCount ()).

self.stockItemModel.appendItem(note, list[note]) Add an item from the obtained dictionary (list). See the [AppendItem](### Add Item (appendItem)) created earlier.

Initial loading

		#Initial loading
		self.refresh()

Get Notes the last time you launch it. It has the same behavior as [refresh](### refresh button behavior), so we will reuse it.

in conclusion

There were many descriptions of PyQt this time, but it was quite difficult to collect information online. If the official documentation is a little easier to read. .. But I feel like I've finally learned a little about how to use PySide.

Reference list

-[Maya Python] Crushing the contents of the script 1 ~ Camera Speed Editor -Qt Selection Mode Note -[[Python coding convention] Decipher PEP8-Class name](https://qiita.com/simonritchie/items/bb06a7521ae6560738a7#%E3%82%AF%E3%83%A9%E3%82%B9%E5% 90% 8D)

Recommended Posts

[Maya Python] Crush the contents of the script 2 ~ list Notes
[Maya Python] Crush the contents of the script 3 ~ List unknown Plugins
[Maya Python] Crush the contents of the script 1 ~ Camera Speed Editor
Template of python script to read the contents of the file
About the basics list of Python basics
A Python script that compares the contents of two directories
Learning notes from the beginning of Python 1
Learning notes from the beginning of Python 2
[Python] A program that rotates the contents of the list to the left
[Introduction to Python] How to sort the contents of a list efficiently with list sort
[python] Check the elements of the list all, any
[Python] Sort the list of pathlib.Path in natural sort
The contents of the Python tutorial (Chapter 5) are itemized.
The contents of the Python tutorial (Chapter 4) are itemized.
The contents of the Python tutorial (Chapter 2) are itemized.
The contents of the Python tutorial (Chapter 8) are itemized.
The contents of the Python tutorial (Chapter 1) are itemized.
Make a copy of the list in Python
The contents of the Python tutorial (Chapter 10) are itemized.
The contents of the Python tutorial (Chapter 6) are itemized.
The contents of the Python tutorial (Chapter 3) are itemized.
List of python modules
Python script to get a list of input examples for the AtCoder contest
[python] Get the list of classes defined in the module
Get the return code of the Python script from bat
Not being aware of the contents of the data in python
[Python] Get the list of ExifTags names of Pillow library
[Python] Outputs all combinations of elements in the list
Towards the retirement of Python2
About the ease of Python
[Python] Copy of multidimensional list
About the features of Python
Simulation of the contents of the wallet
The Power of Pandas: Python
Try to get the function list of Python> os package
Get the number of specific elements in a python list
[Data science memorandum] Confirmation of the contents of DataFrame type [python]
2015-11-26 python> Display the function list of the module> import math> dir (math)
How to connect the contents of a list into a string
Process the contents of the file in order with a shell script
The story of Python and the story of NaN
[Python] The stumbling block of import
First Python 3 ~ The beginning of repetition ~
[Python] Display only the elements of the list side by side [Vertical, horizontal]
[python, ruby] fetch the contents of a web page with selenium-webdriver
[Python] [Table of Contents Links] Python Programming
pyenv-change the python version of virtualenv
Output the contents of ~ .xlsx in the folder to HTML with Python
Get the script path in Python
Change the Python version of Homebrew
[python] Get the rank of the values in List in ascending / descending order
See the contents of Kumantic Segumantion
Python Math Series ⓪ Table of Contents
[Python] Understanding the potential_field_planning of Python Robotics
Review of the basics of Python (FizzBuzz)
python note: map -do the same for each element of the list
Introductory table of contents for python3
List of disaster dispatches from the Sapporo City Fire Department [Python]
Learn the basics of Python ① Beginners
Python> sys.path> List of strings indicating the path to search for modules
Receive a list of the results of parallel processing in Python with starmap