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.
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.
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.
# -*- 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()
It looks like this. It is easy to understand if you draw a blueprint to understand the operation of the tool.
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
#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.
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.
In this way, QTreeView sets the appearance and behavior of the tree view.
#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
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.
#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, and
rolespecifies the role. Specify the format suitable for editing with
QtCore.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.
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 ()`.
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.
#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
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
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
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.
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.
-[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