Random python notes
This is more or less a dump of all the Stackoverflow questions that I hit until I know them by heart.
QT/ PySide generic stuff
http://www.sidefx.com/docs/houdini14.0/hom/cookbook/qt/
http://doc.qt.io/qt-4.8/widgets-and-layouts.html
Sensible class output
Until further notice, I'll try adding something like this to classes:
def __repr__(self):
output = '\n{} class\n'.format(self.__class__.__name__)
for k, v in self.__dict__.iteritems():
output += '\t{}: {}\n'.format(k, v)
return output
# as above, get class name as string
self.__class__.__name__
Where's my home?
from os.path import expanduser
home = expanduser("~")
Arguments with argparse
Argparse is cool to parse all sorts of arguments.
Basic usage:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--wiggle", help="Perform wiggle.")
parser.add_argument("bodypart", help="Required. Valid values are Bum, Arm or Wiener")
# a flag
parser.add_argument('-w', action='store_true')
args = parser.parse_args()
print args.wiggle
Exclusive mandatory arguments with optional values (think of git push, git pull --rebase)
import argparse
# Parse the arguments //////////////////////////////////////////////////////////
parser = argparse.ArgumentParser(prog='SUBCOMMAND')
subparsers = parser.add_subparsers(dest='action')
parser_one = subparsers.add_parser('action_one')
parser_one.add_argument('one_argument', type=str)
parser_pull = subparsers.add_parser('pull')
args = parser.parse_args()
# Handle the arguments /////////////////////////////////////////////////////////
if args.action == 'action_one':
print 'action one called with arg', args.one_argument
elif args.action == 'pull':
print 'pull action called'
Display help by default:
before parsing args:
[...]
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
[...]
List comprehensions
words = ['I', 'like', 'my', 'Currywurst', 'with', 'fries', 'and', 'hot', 'sauce']
print [w.upper() for w in words]
>>> ['I', 'LIKE', 'MY', 'CURRYWURST', 'WITH', 'FRIES', 'AND', 'HOT', 'SAUCE']
print [[w.upper(), len(w)] for w in words]
>>> [['I', 1], ['LIKE', 4], ['MY', 2], ['CURRYWURST', 10], ['WITH', 4], ['FRIES', 5], ['AND', 3], ['HOT', 3], ['SAUCE', 5]]
# Also cool:
print [w.upper() for w in words if w.startswith('a')]
>>> ['AND']
Dict comprehensions
Almost as above...
mydict = {key: value for (key, value) in some_list}
Colorize output
def colorize(msg, color='green'):
names = {
'purple': '\033[95m',
'blue': '\033[94m',
'green': '\033[92m',
'warning': '\033[93m',
'error': '\033[91m',
'ENDC': '\033[0m',
'bold': '\033[1m',
'underline': '\033[4m'
}
if color in names:
return '{}{}{}'.format(names[color], msg, names['ENDC'])
else:
return msg
Usage:
a = 10
msg = 'Hello'
print '{} is {}'.format(colorize(msg), colorize(a, color='error'))
Enumerate is good
my_list = ['apple', 'banana', 'grapes', 'pear']
# yes:
for c, value in enumerate(my_list, 1):
print(c, value)
# no:
i=1
for c in my_list:
print c, i
i += 1
Set class attribute by string
This should be easy, right? Sometimes you need to set an attribute based on a string value.
These are some ways to do it:
class ClassOne():
def __init__(self):
self.val = None
self.__dict__['val'] = 'yo'
class ClassTwo():
def __init__(self):
self.val = None
setattr(self, 'val', 'what up')
c1 = ClassOne()
c2 = ClassTwo()
print c1.val, c2.val
>>> yo what up
c1.__dict__['val'] = 'hey'
setattr(c2, 'val', 'dude')
print c1.val, c2.val
>>> hey dude
Class validity iteration
Sometimes I like to set class attributes first in a class. This is how I test if they have been set successfully:
class Foo(object):
def __init__(self):
self.foo = 'elephant'
self.bar = None
def is_valid(self):
return all([a is not None for a in self.__dict__.values()])
f = Foo()
print f.is_valid()
Traversing folders and files recursively
optionally list certain types
relevant_filetypes = ['.txt', '.py', '.json']
for root, subdirs, files in os.walk('/tmp'):
for f in files:
if os.path.splitext(f)[1] in relevant_filetypes:
print os.path.join(root, f)
# how big is a file?
import os
def size(filepath):
return os.stat(filepath).st_size
with
with will allow you to operate on something in a controlled init and end condition (or lifecyle) that allows you to set a defined condition just for one operation. You don't need __init__()
for this to work, but often you might want to pass an argument, too.
class Condition():
def __init__(self, arg):
self.arg = arg
def __enter__(self):
print 'doing sth with', self.arg
def __exit__(self, type, value, traceback):
print 'done'
with Condition('option'):
#do some stuff
pass
For example, you could retain the current selection:
class KeepSelection():
def __enter__(self):
self.selection = pm.selected()
def __exit__(self, type, value, traceback):
pm.select(self.selection)
with KeepSelection():
pm.polyCube()
Invoking pm.polyCube() would normally cause you to lose the selection. Because we store the selection in the __enter__()
function, we can later restore them in the __exit__()
function which will trigger after the last operation inside the with statement. Super handy sometimes.
Misc stuff
# quick and proper formatting
print 'a is {0}, b is {1}'.format(a, b)
# call me dumb, did't know that.
test = float('inf')
# to store a minimum value
for i in sth:
if i < test : test = i
# Whitespace collapsing:
_str = 'Hello little fella !'
print _str.split()
# or for the whole thing
" ".join(_str.split())
# JSON: write to file
import json
results = [1, 2, 3]
with open('log.json', 'w') as f:
json.dump(results, f, indent=4)
# JSON: load from file
with open('log.json') as logfile:
log_data = json.load(logfile)
print log_data
# get file extensiom
filename, file_extension = os.path.splitext('/path/to/somefile.ext')
#or..
ext = os.path.splitext('/path/to/somefile.ext')[1]
# join list elements into a space-separated string
list = ['ham', 'eggs', 'lazors']
' '.join(list)
# does the file exist?
import os.path
os.path.isfile(fname)
#is file executable?
os.access(fpath, os.X_OK)
# set executable bit
import os
import stat
st = os.stat('somefile')
os.chmod('somefile', st.st_mode | stat.S_IEXEC)
Simplistic threading
import time # Just for the sleeping part
from multiprocessing.dummy import Pool as ThreadPool
images=['1.img', '2.img', '3.img', '4.img', '5.img']
def myExpensiveFunction(arg):
time.sleep(3)
print arg
pool = ThreadPool(4) # use 4 threads
results = pool.map(myExpensiveFunction, images)
VirtualEnv and PySide (OSX)
Especially for pyside, virtualenv is a great way not to mess with system pyside and screwing up maya. It seems you can't just install pyside with pip here, so we need to build from source. Fortunately, this is quite straightforward. Afterwards you're rewarded with a tiny independent sanbox to do your pyside gui apps in. Thanks to Fredrik Averpil who did a lot of digging.
# this is only needed if you don't have these dependencies:
brew install python qt cmake
sudo pip install myenv
virtualenv myenv
source myenv/bin/activate
cd myenv
git clone --recursive https://github.com/PySide/pyside-setup.git pyside-setup
cd pyside-setup
python setup.py bdist_wheel --ignore-git
cd dist
pip install PySide*.whl
Package PySide app
With the above in effect, it is rather straightforward to package your (gui) app to a standalone executable.
YMMV, but I had to copy myenv/lib/python2.7/site-packages/PySide/lib* to myenv/lib. Try without that first.
# first, install pyinstaller into virtualenv.
pip install pyinstaller
# run that thang
bin/pyinstaller -y --hiddenimport json --osx-bundle-identifier com.my.app -w pythonfile.py
# -y : don't warn to overwrite existing files
# --hiddenimport: if you run into trouble because of missing imports list them here.
# --osx-bundle-identifier: On osx, this will be set for the app bundle
# -w on osx, triggers the build of an *.app. Win/OSX will not show a console window.
Cookbook
Resize images with PIL
#!/usr/bin/env python
import os
import sys
import argparse
from PIL import Image
class Resize(object):
def __init__(self, path):
self.maxsize = (1024, 1024)
self.square = True
self.path = path
self.extensions = ['.tga', '.png', '.jpg']
self.dryrun = False
def collect_img(self):
if os.path.isdir(self.path):
for root, dirs, files in os.walk(self.path):
for file_ in files:
for ext in self.extensions:
if ext.lower() in file_.lower():
yield os.path.join(root, file_)
def run(self):
for img_path in self.collect_img():
# print img_path
img = Image.open(img_path)
img_basename = os.path.basename(img_path)
if img.size[0] > self.maxsize[0] or img.size[1] > self.maxsize[1]:
print '{} too large: {}px of {}px'.format(img_basename, img.size, self.maxsize)
print 'Source info:', img.mode, img.size, img.format
if self.dryrun:
continue
try:
new_img = img.resize(self.maxsize)
# img.thumbnail((self.maxsize, self.maxsize))
new_img.save(img_path, img.format)
print 'Successfully wrote', img_path
except Exception as e:
print 'Could not write', img_path, e
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='resizer')
parser.add_argument('folder', type=str)
parser.add_argument('maxsize', type=int)
args = parser.parse_args()
# Handle the arguments ////////////////////
resizer = Resize(args.folder)
s = int(args.maxsize)
size = (s, s)
resizer.maxsize = size
resizer.run()