[Maya Python] Crush the contents of the script 1 ~ Camera Speed Editor

Purpose of this article

Deepen your understanding while checking the contents of your own tools one by one, and use them for future tool development. 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.

Tool overview

Slows the camera movement speed in Maya and allows fine adjustment of the camera. Maya cameras can be subtly fast, which can be a bit annoying for delicate camera work. Normally, it is adjusted with Tool Settings, but it is difficult to use because there are settings for each camera tool, so I unified it. Turn the dial to the left to slow down the speed, and turn it to the right to speed up, and you can switch between camera tools.

cameraSpeedEditor.gif

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

Full code

cameraSpeedEditor.py


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

class Widget(QtWidgets.QWidget):
    def __init__(self):
        #Call parent method
        super(Widget, self).__init__()

        #Place grid layout
        layout = QtWidgets.QGridLayout(self)

        #Description label
        labelWidget = QtWidgets.QLabel(u'Adjust the speed of the camera\n\n ← slow camera speed adjustment speed →')
        labelWidget.setAlignment(Qt.AlignCenter)
        layout.addWidget(labelWidget,0,0,1,-1)

        #Dial widget
        self.camDial = QtWidgets.QDial()
        self.camDial.setRange(0, 100)
        self.camDial.setValue(50)
        self.camDial.setNotchesVisible(True)
        self.camDial.sliderReleased.connect(self.setCamSpeed)
        layout.addWidget(self.camDial,1,0,1,-1)

        #Check super slow mode
        self.checkWidget = QtWidgets.QCheckBox(u'Super slow')
        self.checkWidget.setChecked(False)
        self.checkWidget.stateChanged.connect(self.setCamSpeed)
        layout.addWidget(self.checkWidget,2,0)

        #Camera tool combo box
        setCamLabel = QtWidgets.QLabel('set Camera tool:')
        setCamLabel.setAlignment(Qt.AlignRight)
        layout.addWidget(setCamLabel,3,0)
        selCamItem = QtWidgets.QComboBox(self)
        selCamItem.addItem('tumble')
        selCamItem.addItem('track')
        selCamItem.addItem('dolly')
        selCamItem.addItem('boxZoomSuper')
        selCamItem.addItem('roll')
        selCamItem.addItem('yawPitch')
        selCamItem.activated[str].connect(self.setTool)
        layout.addWidget(selCamItem,3,1)

        #Reset button
        resetButton = QtWidgets.QPushButton(u'reset')
        resetButton.clicked.connect(self.resetCamSpeed)
        layout.addWidget(resetButton,4,0,1,-1)

    #Camera speed set
    def setCamSpeed(self):
        #Calculate speed numbers
        value = float(self.camDial.value()) **3*0.0000072+0.1
        if self.checkWidget.isChecked():
            value = value /10

        #Set the calculated value
        cmds.tumbleCtx( 'tumbleContext'             ,e=True ,ts=value)
        cmds.trackCtx(  'trackContext'              ,e=True ,ts=value)
        cmds.dollyCtx(  'dollyContext'              ,e=True ,s =value)
        cmds.boxZoomCtx('boxZoomContext'            ,e=True ,zs=value)
        cmds.rollCtx(   'rollContext'               ,e=True ,rs=value)
        cmds.orbitCtx(  'azimuthElevationContext'   ,e=True ,orbitScale=value)
        cmds.orbitCtx(  'yawPitchContext'           ,e=True ,orbitScale=value)

        #Show what you have set
        print ( '# Camera speed is '+str(value)+' !\n'),

    #Camera toolset
    def setTool(self, selectTool):
        cmds.setToolTo('%sContext' %(selectTool))
        print ( 'Set %s Tool !\n' %(selectTool)),

    #Camera speed reset
    def resetCamSpeed(self):
        #Reset the value of each camera to the default
        cmds.tumbleCtx( 'tumbleContext'             ,e=True ,ts=1)
        cmds.trackCtx(  'trackContext'              ,e=True ,ts=1)
        cmds.dollyCtx(  'dollyContext'              ,e=True ,s =1)
        cmds.boxZoomCtx('boxZoomContext'            ,e=True ,zs=1) 
        cmds.rollCtx(   'rollContext'               ,e=True ,rs=1)
        cmds.orbitCtx(  'azimuthElevationContext'   ,e=True ,orbitScale=1)
        cmds.orbitCtx(  'yawPitchContext'           ,e=True ,orbitScale=1)
        self.camDial.setValue(50)
        self.checkWidget.setChecked(False)
        print ( '# Camera speed is Default !\n'),


class MainWindow(MayaQWidgetBaseMixin, QtWidgets.QMainWindow):
    def __init__(self):
        #Call parent method
        super(MainWindow, self).__init__()

        #Specify the name and size of the window
        self.setWindowTitle('Camera Speed editor')
        self.resize(200, 250)

        #Instance Widget and place it in window
        widget = Widget()
        self.setCentralWidget(widget)

    def closeEvent(self, event):
        #Perform camera speed reset when window is closed
        widget = Widget()
        widget.resetCamSpeed()


def main():
    #Window display
    window = MainWindow()
    window.show()

#Processing when called in main
if __name__ == "__main__":
    main()
	

Let's see the details

From here, I would like to explain how each code works, starting from the beginning, while understanding for myself.

Be sure to write it in your head

# -*- coding: utf-8 -*-

If you use Japanese in the code, you should put it in. See here for details. It seems rather deprecated in Python3. Maya is still 2.7, so I'll add it just in case.

The name of the character code "Are" written at the beginning of the sentence in Python (something like UTF-8)

Library import

from maya.app.general.mayaMixin import MayaQWidgetBaseMixin
from PySide2 import QtWidgets
from PySide2.QtCore import Qt
from maya import cmds

Import each library to use. Python has a lot of libraries, so you can do various things by using it. I think it's one of the advantages of using Python instead of MEL.

from 〇〇 import △△ means to bring △△ in 〇〇. Functions of each library. ** MayaQWidgetBaseMixin **: Used to set the parent-child relationship of the window ** QtWidgets, Qt **: PySide, UI related functions ** cmds **: maya command

The 4th line from maya import cmds is a reference of maya, It is described as ʻimport maya.cmds as cmds`. I'm doing this because it's more beautiful to have them all from. What you are doing is probably the same.

Introduction to Python Modules and Imports from

Widget settings

class Widget(QtWidgets.QWidget):
    def __init__(self):
        #Call parent method
        super(Widget, self).__init__()

        ...

class。 The explanation of class is broken here. It's complicated, so please understand it in a book. The place where I stumbled the most when I was doing Python. I wonder if I should get used to it while using it.

I also thought that I was studying myself, It is recommended that you read a book because there is a limit to using only online articles. Personally, [Introduction to O'Reilly Japan Python3](https://www.amazon.co.jp/%E5%85%A5%E9%96%80-Python-3-Bill-Lubanovic/dp/4873117380/ref = sr_1_1? __ mk_ja_JP =% E3% 82% AB% E3% 82% BF% E3% 82% AB% E3% 83% 8A & dchild = 1 & keywords =% E5% 85% A5% E9% 96% 80python3 & qid = 1598490946 & sr = 8-1 ) Is very easy to understand. I'm still reading. I think it is for beginners to intermediates.

[Introduction to O'Reilly Japan Python3](https://www.amazon.co.jp/%E5%85%A5%E9%96%80-Python-3-Bill-Lubanovic/dp/4873117380/ref=sr_1_1? __mk_ja_JP =% E3% 82% AB% E3% 82% BF% E3% 82% AB% E3% 83% 8A & dchild = 1 & keywords =% E5% 85% A5% E9% 96% 80python3 & qid = 1598490946 & sr = 8-1) [Illustration] What is object-oriented? (Meaning of class method instance)

Let's look at each line

class Widget(QtWidgets.QWidget):

Inherit ** QtWidgets.QWidget ** imported from the library to ** Widget **. It's like preparing to customize the base of PySide2 for yourself.

    def __init__(self):

__init__ Special method. Initialize the part that is called first when the instance is created. You'll see it most often when you look at the class code.

self First argument. It works for other than self, but by convention ** self ** is always used. These rules are summarized in the document *** PEP 8 ***, so please refer to it. Perhaps if you are self-taught on the net, you will not be able to reach these rules and you will be able to create your own rules (self-discipline)

Understanding Python self and init What is Python self? Explanation of usage and precautions Understanding [Python Coding Standards] PEP8

        #Call parent method
        super(Widget, self).__init__()

Another confusing description. I didn't understand it at first, but it's nothing if I understand it. Inherit ** QtWidgets.QWidget ** to ** Widget ** and If you create an initialization method with __init__, the initialization method of the parent (QtWidgets.QWidget) will not be called. So, put this description and call the parent initialization method. By the way, this description was necessary in python2 series, but in 3 series, super () .__ init__ () is good. simple. I'm wondering when Maya will switch to 3 series. ..

[Python] class inheritance (super) How to use Python's super () function

I tried to illustrate the movement so far

To better understand what the class is doing, let's illustrate what's going on. cameraSpeedEditor__class.jpg 2.From ~ imports PySide2.QtWidgets 7. Inherit the imported QtWidgets QWidget with the name Widget 8. Create initialization method in Widget class 10. Call the initialization method of the imported QtWidgets.QWidget 13 or later Create UI layout and functions

There are still many letters, so I made a simple illustration. class_explain.jpg In this way, you can make an instance copy of the original blueprint (familiar with Maya) and override (overwrite) the arms, legs, and colors to get the shape you want. So, if you overwrite it with __init__, the original __init__ will not be called, so you need super.

UI creation

Well, the explanation of class has become long, but I will finally create the UI from now on.

        #Place grid layout
        layout = QtWidgets.QGridLayout(self)

Create QGridLayout. Buttons and labels will be placed in this grid layout.

        #Description label
        labelWidget = QtWidgets.QLabel(u'Adjust the speed of the camera\n\n ← slow camera speed adjustment speed →')

Added labels to grid layout. When using Japanese such as ʻu'character', prefix it with ** u **. You can start a new line with \ n`.

        labelWidget.setAlignment(Qt.AlignCenter)

Place the created label widget in the center.

        layout.addWidget(labelWidget,0,0,1,-1)

Label widget grid layout 0th row, 0th column, 1st row length, **-1 column length ** If you want to use it from the specified location to the end, use ** -1 **.

grid_explain.jpg In this way, you can specify from which position in the row or column how long it is.

        #Dial widget
        self.camDial = QtWidgets.QDial()
        self.camDial.setRange(0, 100)
        self.camDial.setValue(50)
        self.camDial.setNotchesVisible(True)
        self.camDial.sliderReleased.connect(self.setCamSpeed)
        layout.addWidget(self.camDial,1,0,1,-1)

Create a dial widget. This time, self.camDial and self are added because I want to pull the numerical value from another place. Specify the range from 0 to 100. Changed the default value to 50. Add scale with setNotchesVisible. With self.camDial.sliderReleased.connect (self.setCamSpeed), Set self.setCamSpeed to be executed when the mouse button is released.

        #Check super slow mode
        self.checkWidget = QtWidgets.QCheckBox(u'Super slow')
        self.checkWidget.setChecked(False)
        self.checkWidget.stateChanged.connect(self.setCamSpeed)
        layout.addWidget(self.checkWidget,2,0)

Create a check widget. Specify that the default is unchecked and that self.setCamSpeed is executed when the checked state is changed. The layout is arranged in the 2nd row and 0th column. If both lengths are 1, you can leave it blank.

        #Camera tool combo box
        setCamLabel = QtWidgets.QLabel('set Camera tool:')
        setCamLabel.setAlignment(Qt.AlignRight)
        layout.addWidget(setCamLabel,3,0)
        selCamItem = QtWidgets.QComboBox(self)
        selCamItem.addItem('tumble')
        selCamItem.addItem('track')
        selCamItem.addItem('dolly')
        selCamItem.addItem('boxZoomSuper')
        selCamItem.addItem('roll')
        selCamItem.addItem('yawPitch')
        selCamItem.activated[str].connect(self.setTool)
        layout.addWidget(selCamItem,3,1)

selCamItem = QtWidgets.QComboBox (self) Create a combo box. Add multiple camera tool items with ʻaddItem. I want to use the name as it is, so it conforms to the description of Maya's camera tool. selCamItem.activated [str] .connect (self.setTool)When the activity changes Execute by passing ** string ** of addItem toself.setTool`.

        #Reset button
        resetButton = QtWidgets.QPushButton(u'reset')
        resetButton.clicked.connect(self.resetCamSpeed)
        layout.addWidget(resetButton,4,0,1,-1)

Creating a reset button. Set the operation settings and layout in the same way as others.

This completes the UI creation.

Create the behavior when operating the UI

Create the operation contents of ** ~ .connect ** that appeared when creating the UI.

    #Camera speed set
    def setCamSpeed(self):
        #Calculate speed numbers
        value = float(self.camDial.value()) **3*0.0000072+0.1
        if self.checkWidget.isChecked():
            value = value /10

        #Set the calculated value
        cmds.tumbleCtx( 'tumbleContext'             ,e=True ,ts=value)
        cmds.trackCtx(  'trackContext'              ,e=True ,ts=value)
        cmds.dollyCtx(  'dollyContext'              ,e=True ,s =value)
        cmds.boxZoomCtx('boxZoomContext'            ,e=True ,zs=value)
        cmds.rollCtx(   'rollContext'               ,e=True ,rs=value)
        cmds.orbitCtx(  'azimuthElevationContext'   ,e=True ,orbitScale=value)
        cmds.orbitCtx(  'yawPitchContext'           ,e=True ,orbitScale=value)

        #Show what you have set
        print ( '# Camera speed is '+str(value)+' !\n'),

Get the status of dials and checkboxes, Reflect the calculated value in each camera tool.

value = float (self.camDial.value ()) ** 3 * 0.0000072 + 0.1 Convert from dial value to camera speed value. Why is it such a strange calculation formula, but when I adjusted it to move just right, it became like this.

ʻIf ~` reduces ** value ** to 1/10 if super slow mode is checked.

# Set the calculated value below Reflect the calculated value in the value of each camera tool The reason why there are many wasted margins is to align the rows and make them easier to see. It seems that PEP8 is not recommended, but I dare to ignore it. How is it actually?

print ('hogehoge'), The last **, (comma) ** are added for display on the Maya command line. Because warnings are colored and a little overwhelming. For now, it's a mystery why it can be displayed on the command line with a comma. I wonder if it's a Maya specification.

    #Camera toolset
    def setTool(self, selectTool):
        cmds.setToolTo('%sContext' %(selectTool))
        print ( 'Set %s Tool !\n' %(selectTool)),

** [str] ** received from selCamItem.activated [str] .connect (self.setTool), cmds.setToolTo ('% sContext'% (selectTool)) Switch with this command.

% s replaces% (Honyara) Example: '%sContext' %('tumble')'tumbleContext'

    #Camera speed reset
    def resetCamSpeed(self):
        #Reset the value of each camera to the default
        cmds.tumbleCtx( 'tumbleContext'             ,e=True ,ts=1)
        cmds.trackCtx(  'trackContext'              ,e=True ,ts=1)
        cmds.dollyCtx(  'dollyContext'              ,e=True ,s =1)
        cmds.boxZoomCtx('boxZoomContext'            ,e=True ,zs=1) 
        cmds.rollCtx(   'rollContext'               ,e=True ,rs=1)
        cmds.orbitCtx(  'azimuthElevationContext'   ,e=True ,orbitScale=1)
        cmds.orbitCtx(  'yawPitchContext'           ,e=True ,orbitScale=1)
        self.camDial.setValue(50)
        self.checkWidget.setChecked(False)
        print ( '# Camera speed is Default !\n'),

Restore camera settings and UI to defaults.

With the above, the operation when operating the widget has been created.

Creating a window

class MainWindow(MayaQWidgetBaseMixin, QtWidgets.QMainWindow):
    def __init__(self):
        #Call parent method
        super(MainWindow, self).__init__()

        #Specify the name and size of the window
        self.setWindowTitle('Camera Speed editor')
        self.resize(200, 250)

        #Instance Widget and place it in window
        widget = Widget()
        self.setCentralWidget(widget)

Inheriting ** MayaQWidgetBaseMixin ** and ** QtWidgets.QMainWindow ** ** MayaQWidgetBaseMixin ** is a feature that prevents windows from going under the main Maya window. I used to use shiboken etc., but it feels good to be much simpler.

New! Parent-child relationship in Maya windows MayaQWidgetBaseMixin!

Specify the window title and window size. Place the way jet you made earlier in the center of the window.

    def closeEvent(self, event):
        #Perform camera speed reset when window is closed
        widget = Widget()
        widget.resetCamSpeed()

Reset the camera speed when the window is closed. You can use def closeEvent (self, event): to add additional behavior when the window is closed.

Now you have created the UI and main functions.

Window display

def main():
    #Window display
    window = MainWindow()
    window.show()

If you hit main () and the command, a window will be displayed and you can execute the program.

Main run time behavior

#Processing when called in main
if __name__ == "__main__":
    main()

This is also like magic. Do not run the program when importing. If you copy and paste the entire code into a script editor and execute it, the program will start.

[Python] if name =='main': What is it?

in conclusion

At first I thought it was just a memo, but it has become quite long. If I add something I don't understand while writing in places, it's like this.

It would be good for me to leave an article like this every time I write a tool. After all, I think that studying is something that you can learn by teaching → trying → teaching.

There may be many places that cannot be reached, but please forgive me. I would appreciate it if you could tell me if there are any mistakes.

Reference list

Below is the site link that I used as a reference.

The name of the character code "Are" written at the beginning of the sentence in Python (something like UTF-8) Introduction to Python Modules and Imports from [Illustration] What is object-oriented? (Meaning of class method instance) Understanding Python self and init What is Python self? Explanation of usage and precautions Understanding [Python Coding Standards] PEP8 [Python] Class Inheritance (super) How to use Python's super () function new! Parent-child relationship in Maya windows MayaQWidgetBaseMixin! [Python] if name == What is'main':?

Recommended Posts

[Maya Python] Crush the contents of the script 1 ~ Camera Speed Editor
[Maya Python] Crush the contents of the script 2 ~ list Notes
[Maya Python] Crush the contents of the script 3 ~ List unknown Plugins
Template of python script to read the contents of the file
A Python script that compares the contents of two directories
Get the contents of git diff from python
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 10) are itemized.
Compare the speed of Python append and map
The contents of the Python tutorial (Chapter 6) are itemized.
The contents of the Python tutorial (Chapter 3) are itemized.
the zen of Python
Get the return code of the Python script from bat
Not being aware of the contents of the data in python
python3 Measure the processing speed.
Towards the retirement of Python2
About the ease of Python
About the features of Python
[Data science memorandum] Confirmation of the contents of DataFrame type [python]
Simulation of the contents of the wallet
The Power of Pandas: Python
I compared the speed of Hash with Topaz, Ruby and Python
Process the contents of the file in order with a shell script
[python, ruby] fetch the contents of a web page with selenium-webdriver
Output the contents of ~ .xlsx in the folder to HTML with Python
[Python] A program that rotates the contents of the list to the left
Easy encryption of file contents (Python)
[Python] The stumbling block of import
First Python 3 ~ The beginning of repetition ~
[Python] [Table of Contents Links] Python Programming
Understand the contents of sklearn's pipeline
Existence from the viewpoint of Python
pyenv-change the python version of virtualenv
Get the script path in Python
Change the Python version of Homebrew
See the contents of Kumantic Segumantion
Python Math Series ⓪ Table of Contents
Debug the script with Sakura Editor
[Python] Understanding the potential_field_planning of Python Robotics
Review of the basics of Python (FizzBuzz)
Speed comparison of Python XML parsing
Introductory table of contents for python3
About the basics list of Python basics
Learn the basics of Python ① Beginners
I replaced the numerical calculation of Python with Rust and compared the speed
[Introduction to Python] How to sort the contents of a list efficiently with list sort
Get the return value of an external shell script (ls) with python3
I measured the speed of list comprehension, for and while with python2.7.
I checked the contents of docker volume
Change the length of Python csv strings
Script to change the description of fasta
Execute Python script with cron of TS-220
Pass the path of the imported python module
The story of making Python an exe
Learning notes from the beginning of Python 1
Check the existence of the file with python
About the virtual environment of python version 3.7
[Python] Understand the content of error messages
[Python3] Rewrite the code object of the function
I didn't know the basics of Python