Maya undo chunk examples

# -*- coding: utf-8 -*-
##  Copyright 2019 Trevor van Hoof and Jan Pijpers.
##  Licensed under the Apache License, Version 2.0
##  Downloaded from https://janpijpers.com or https://gumroad.com/janpijpers
##  See the license file attached or on https://www.janpijpers.com/script-licenses/

'''
Name: mayaUndoExample
Description:

two undo examples
one on how to use the undo stack as decorator function

and as inside function
'''

from maya import cmds
import traceback
import pymel.core as pm

def undo( function ):
    '''
        This is a function decorator.
        you can use it by writing @undo one line obove any function.
        before the function gets called an undo chunk is started.
        when the function ends it gets closed.
        Be carefull, if you call the function in itself (recursively) it will break the undo stack.
    :param function:
    :return:
    '''
    def funcCall(*args,**kwargs):

        result = None 
        try:
            ## here we open the undo chunk and we give it the name of the fuction
            cmds.undoInfo( openChunk= True, chunkname = function.__name__ )
            result = function( *args,**kwargs )
        except Exception as e:
            ## If we have an error we will print the stack of the error
            print traceback.format_exc()
            ## we also make sure the maya ui shows an error.
            pm.displayError( "## Error, see script editor: %s"%e )
        finally:
            ## we always need to close the chunk at the end else we corrupt the stack.
            cmds.undoInfo( closeChunk = True )
        return result
    return funcCall 



@undo
def simpleExampleWithDecorator():
    '''
        So here we have the decorator defined above the function definition.
        So before this function is called, an undo chunk is created and closed after the function has finished.
    :return:
    '''
    for i in xrange( 10 ):
        loc = cmds.createNode( "spaceLocator"  )
        cmds.xform(loc, translation=(i,i,i))
simpleExampleWithDecorator()


def undoInFunction( ):

    ## its recomended to always use a try and except with an undo chunk else you need to restart maya when it fails.
    ## here we open the undo chunk and we give it the name of the fuction
    cmds.undoInfo(openChunk=True, chunkname="Example")

    try:
        for i in xrange(10):
            loc = cmds.createNode("spaceLocator")
            cmds.xform(loc, translation=(i, i, i) )
    except Exception as e:
        ## If we have an error we will print the stack of the error
        print traceback.format_exc()
        ## we also make sure the maya ui shows an error.
        pm.displayError("## Error, see script editor: %s" % e)
    finally:
        cmds.undoInfo(openChunk=True, chunkname="Example")

undoInFunction()

QT widgets under mouse

# -*- coding: utf-8 -*-
##  Copyright 2019 Trevor van Hoof and Jan Pijpers.
##  Licensed under the Apache License, Version 2.0
##  Downloaded from https://janpijpers.com or https://gumroad.com/janpijpers
##  See the license file attached or on https://www.janpijpers.com/script-licenses/

'''
Name: widgetUnderMouse
Description:

prints and highlights the widget under your mouse.
note that this is supppaaaaaaa hacky! And it will make any qt ui flash and slow down a lot!
But its helpfull when you want to find out what an object is called. I used this a lot in Maya to hack into the UI and add custom widgets.

If you click with the mouse buttons the overlay will stop.

'''

import sys

## Simple pyside 2 or 1 import check.
try:
    PYSIDE_VERSION = 2
    from PySide2.QtWidgets import *
    from PySide2.QtGui import *#QFont, QIcon, QPixmap, QColor
    from PySide2.QtCore import * 
    from PySide2.QtUiTools import * 
    from pyside2uic import compileUi
except:
    from PySide.QtCore import * 
    from PySide.QtGui import * 



def widgets_at(pos, topOnly = False ):
    """Return ALL widgets at `pos`
        It uses the WA_TransparentForMouseEvents trick to find the underlying widgets.
    Arguments:
        pos (QPoint): Position at which to get widgets
    """

    widgets = []
    ## Ask QT what widget is at this position
    widget_at = QApplication.widgetAt(pos)
    if topOnly:
        return [widget_at]
        
    while widget_at:
        widgets.append(widget_at)
        ## Make widget invisible, so the next time we call the widgetAt function
        ## QT will return the underlying widget.
        widget_at.setAttribute(Qt.WA_TransparentForMouseEvents)
        widget_at = QApplication.widgetAt(pos)

    # Restore attributes else nothing will respond to mouse clicks anymore.
    for widget in widgets:
        widget.setAttribute(Qt.WA_TransparentForMouseEvents, False)

    return widgets
    
class Overlay(QWidget):
    def __init__(self, parent=None):
        '''
            This is an overlay that sits across the entire UI.
            This way its easier to track the mouse position and interact with the widgets below it.
        :param parent:
        '''
        super(Overlay, self).__init__(parent)
        self.setAttribute(Qt.WA_StyledBackground)
        self.setStyleSheet("QWidget { background-color: rgba(0, 255, 0, 0) }")
        self.setMouseTracking(True)
        self._widgetsUnderMouse = set()

    def mouseMoveEvent(self, event):
        '''
            For every 'pixel' we move our mouse this function is called.
        :param event:
        :return:
        '''
        ## query the current position
        pos = QCursor.pos()

        ## Find the widgets below the cursor.
        currentWidgets = set( [ widgets_at(pos)[1] ] )

        ## If we have found new widgets.
        if currentWidgets != self._widgetsUnderMouse:
            ## Remove the old outline of the widgets we had before
            self._removeOutline(self._widgetsUnderMouse)
            ## Add a new outline to our new widgets
            self._addOutline(currentWidgets)

            ## Print all widgets we have under our mouse now.
            for w in currentWidgets:
                n = w.objectName()
                print "Name: ",n, "Widget: ", w
            self._widgetsUnderMouse = currentWidgets
        ## Let qt do the rest of its magic.
        return super(Overlay, self).mouseMoveEvent(event)
        
    def mousePressEvent( self, event ):
        '''
            If we click with the left mouse button the overlay stops.
        :param event:
        :return:
        '''
        self.deleteLater()
        return super(Overlay, self).mousePressEvent(event)
        
        
    def _addOutline( self, wList ):
        for w in wList:
            n = w.objectName()
            ## SUUUPER HACK TRICK
            ## We force an object name on the object
            w.setObjectName("AAAAAAA")
            ## Make the object have a red outline with a stylesheet
            w.setStyleSheet('QWidget#AAAAAAA {border: 4px solid red;outline-offset: -2px;}')
            ## Restore the object name
            w.setObjectName(n)
            

    def _removeOutline( self, wList):
        '''
            Not the best idea because we remove all style info,
            actually we should store the style sheet info before setting the outline buuuuuttt you get the idea :D
        :param wList:
        :return:
        '''
        for w in wList:
            w.setStyleSheet("")
    
    def _clearAll(self):
        self._removeOutline(self._widgetsUnderMouse)
    
    def __del__(self):
        self._clearAll()
        self._widgetsUnderMouse = set()
    
def get_maya_window():
    for widget in QApplication.allWidgets():
        try:
            if widget.objectName() == "MayaWindow":
                return widget
        except:
            pass
    return None 
    
    
window = get_maya_window()
app = None 
if not window:
    '''
        If we are not in Maya, we just make an example window 
    '''
    app = QApplication(sys.argv)
    
    window = QWidget()
    window.setObjectName("Window")
    window.setFixedSize(200, 100)

    button = QPushButton("Button 1", window)
    button.setObjectName("Button 1")
    button.move(10, 10)
    button = QPushButton("Button 2", window)
    button.setObjectName("Button 2")
    button.move(50, 15)
    
overlay = Overlay(window)
overlay.setObjectName("Overlay")
overlay.setFixedSize(window.size())
overlay._clearAll()
overlay.show()

if app:
    window.show()
    app.exec_()

QT settings example

# -*- coding: utf-8 -*-
##  Copyright 2019 Trevor van Hoof and Jan Pijpers.
##  Licensed under the Apache License, Version 2.0
##  Downloaded from https://janpijpers.com or https://gumroad.com/janpijpers
##  See the license file attached or on https://www.janpijpers.com/script-licenses/

'''
Name: qtSettingExample
Description:

Simple example on how to use qt settings.

The first time you run it, it will say you dint have a last opened project, and set an example path
the second time it will print the value of the example path.


'''

from PySide.QtCore import QSettings ## pip install PySide


if __name__ =="__main__":

    settings = QSettings("Company Name", "Tool Name")

    ## Check if the value already exsited
    stored = settings.value("lastOpenedProject")
    if stored:
        print "We have a last opened project: ", stored
    else:
        print "We dint have a last opened project. Setting the example path."
        ## Saven die bende
        settings.setValue("lastOpenedProject", "C:/some/example.path")

    print "Press enter to exit"
    raw_input()

Show QT widget UI hierarchy

# -*- coding: utf-8 -*-
##  Copyright 2019 Trevor van Hoof and Jan Pijpers.
##  Licensed under the Apache License, Version 2.0
##  Downloaded from https://janpijpers.com or https://gumroad.com/janpijpers
##  See the license file attached or on https://www.janpijpers.com/script-licenses/

'''
Name: showQtWidgetHierarchy
Description:

In maya you sometimes want to hack the UI.
Seeng as the UI is made with QT you can hack into it by finding the right node and or widget name.

To make this process easier you can run this script and it will show you the entire hierarchy and return it as a dict

'''


import json

import sys
from PySide.QtGui import * ## pip install PySide

def widgets_recursive(d, widget = None, doPrint =False ):
    if not widget:
        for widget in QApplication.topLevelWidgets():
            get_widget(widget, d, 0, doPrint)
    else:
        get_widget(widget, d, 0, doPrint)
                
def get_widget(w,d, depth = 0, doPrint=False):
    '''
        Recursively searches through all widgets down the tree and prints if desired.
    :param w: the widget to search from
    :param d: the dictionary to add it to
    :param depth: current depth we are at
    :param doPrint: if we need to print
    :return:
    '''
    n = w.objectName()
    n = n if n else str(w)
    if doPrint: print "\t"*depth, n
    newD = {}
    for widget in w.children():
        get_widget(widget, newD, depth +1 )
    d[n] = newD

def get_widget_from_name(name):
    for widget in QApplication.allWidgets():
        try:
            if name in widget.objectName() :
                return widget
        except:
            pass
    return None 

if __name__ =="__main__":

    ## Remove this block if you are running this in maya or something.
    ## Here we make a simple QWindow with a layout and button.
    app = QApplication(sys.argv)
    wid = QWidget()
    wid.setObjectName("myWindow")
    button = QPushButton()
    button.setObjectName("This is my button, there are many like it but this one is mine.")
    lay = QHBoxLayout()
    lay.addWidget(button)
    wid.setLayout(lay)
    wid.show()


    ## Create a simpe dict to hold all the data in the ned.
    widgetHierarchyDict = {}

    ## if you have no idea where to start. Just leave the topWidget argument to None
    ## But if you are in a QT based aplication like Maya you can also start from a widget you know the name of to speed
    ## up the process
    widgetObjectName = None ## "graphEditor1Window"
    topWidget = get_widget_from_name(widgetObjectName)


    ## Recurse over all widgets and store all the information in the provided dict.
    widgets_recursive(widgetHierarchyDict, topWidget)

    ## Print it with json so its nice and clear.
    print json.dumps(widgetHierarchyDict, sort_keys=True, indent = 2 )

Find signals and slots from any QT widget object.

# -*- coding: utf-8 -*-
##  Copyright 2019 Trevor van Hoof and Jan Pijpers.
##  Licensed under the Apache License, Version 2.0
##  Downloaded from https://janpijpers.com or https://gumroad.com/janpijpers
##  See the license file attached or on https://www.janpijpers.com/script-licenses/

'''
Name: findSignalsAndSlotsQt

This script gives you all available signals and slots on a qt widget object.
Normally you can just check the documentation, however if custom signals and slots are used its hard to find them.
We do this by using the meta class from the object.

I used this to find the timechanged event on the maya timeControl widget.

'''
import sys
from PySide.QtGui import * ## pip install PySide
from PySide import QtCore
def get_widget(name):
    '''
        Kind of slow method of finding a widget by object name.
    :param name:
    :return:
    '''
    for widget in QApplication.allWidgets():
        try:
            if name in widget.objectName():
                return widget
        except Exception as e:
            print e
            pass
    return None 


def test( *arg, **kwarg):
    '''
        Simple test function to see what the signal sends out.
    :param arg:
    :param kwarg:
    :return:
    '''

    print "The args are: ", arg
    print "The kwargs are: ", kwarg
    print 

if __name__ == "__main__":

    ## Here we make a simple QLineEdit for argument sake ...
    app = QApplication(sys.argv)
    wid = QLineEdit()
    wid.setObjectName("myLineEdit")
    wid.show()

    ## Find the widget by name.
    ## See the qt ui list hierarchy script to find all widgets in a qt ui.
    widgetObjectName = "myLineEdit"
    widgetObject = get_widget(widgetObjectName)
    if not widgetObject:
        raise Exception("Could not find widget: %s" %widgetObjectName)

    ## Sanity check
    if not wid == widgetObject:
        raise Exception("Should not happen.XD")

    ## Get the meta data from this object
    meta = widgetObject.metaObject()

    ## Itterate over the number of methods available
    for methodNr in xrange(meta.methodCount()):
        method = meta.method(methodNr)
        ## If the method is a signal type
        if method.methodType() == QtCore.QMetaMethod.MethodType.Signal:
            ## Print the info.
            print
            print "This is the signal name", method.signature()
            print "These are the signal arguments: ", method.parameterNames()
            ## If the method is a signal type
        if method.methodType() == QtCore.QMetaMethod.MethodType.Slot:
            ## Print the info.
            print
            print "This is the slot name", method.signature()
            print "These are the slot arguments: ", method.parameterNames()

    '''
    output example:
    ...
    This is the signal name textChanged(QString)
    These are the signal arguments:  [PySide.QtCore.QByteArray('')]
    
    This is the signal name textEdited(QString)
    These are the signal arguments:  [PySide.QtCore.QByteArray('')]
    ...
    
    so now you can do
    
    widgetObject.textChanged.connect(test)
    
    any every time the text changes the 'test' function will be called 
    
    '''
    widgetObject.textChanged.connect(test)

    sys.exit(app.exec_())

Get process ID as dict

Find the offset in seconds between two audio files.

I frequently have to sync audio from a boom mic or external mic and camera audio, and without having a clapboard this is quite tedious (even with a clapboard this is tedious XD) hence this script.

See the attached zipped example files for use with this script.

See http://www.fon.hum.uva.nl/praat/download_win.html
To download the praat tool.

Trim wave file example with scipy

Network data packing and unpacking example

I spend an unreasonable amount of time trying to figure this out. How to pack your data so you can send it across a TCP connection, receive it on the other side, know what data you received and unpack it accordingly. Hence sharing it, hopefully its useful to someone out there ^_^

Zip file example with python.