Python Recipes
From Tech Artists Wiki
SAomIx <a href="http://epewrwauwikg.com/">epewrwauwikg</a>, [url=http://tbspynnqyihn.com/]tbspynnqyihn[/url], [link=http://podwkhkptkwp.com/]podwkhkptkwp[/link], http://wnccfrxwirft.com/
Contents |
[edit] Windows
Examples related to Windows. Most of these require the Win32 Extensions.
[edit] Get Special Windows Folders
Here's how to get various special folders under Windows (User's Desktop, Startup, Common Application Folders, etc.)
# Get current user's Desktop folder path from win32com.shell import shell, shellcon folderPath = shell.SHGetFolderPath(0, shellcon.CSIDL_DESKTOP, None, 0)
For other special folders, replace "CSIDL_DESKTOP" above with the appropriate constant defined in shellcon. The full list includes:
CSIDL_ADMINTOOLS CSIDL_ALTSTARTUP CSIDL_APPDATA CSIDL_BITBUCKET CSIDL_CDBURN_AREA CSIDL_COMMON_ADMINTOOLS CSIDL_COMMON_ALTSTARTUP CSIDL_COMMON_APPDATA CSIDL_COMMON_DESKTOPDIRECTORY CSIDL_COMMON_DOCUMENTS CSIDL_COMMON_FAVORITES CSIDL_COMMON_MUSIC CSIDL_COMMON_OEM_LINKS CSIDL_COMMON_PICTURES CSIDL_COMMON_PROGRAMS CSIDL_COMMON_STARTMENU CSIDL_COMMON_STARTUP CSIDL_COMMON_TEMPLATES CSIDL_COMMON_VIDEO CSIDL_COMPUTERSNEARME CSIDL_CONNECTIONS CSIDL_CONTROLS CSIDL_COOKIES CSIDL_DESKTOP CSIDL_DESKTOPDIRECTORY CSIDL_DRIVES CSIDL_FAVORITES CSIDL_FONTS CSIDL_HISTORY CSIDL_INTERNET CSIDL_INTERNET_CACHE CSIDL_LOCAL_APPDATA CSIDL_MYDOCUMENTS CSIDL_MYMUSIC CSIDL_MYPICTURES CSIDL_MYVIDEO CSIDL_NETHOOD CSIDL_NETWORK CSIDL_PERSONAL CSIDL_PRINTERS CSIDL_PRINTHOOD CSIDL_PROFILE CSIDL_PROGRAMS CSIDL_PROGRAM_FILES CSIDL_PROGRAM_FILESX86 CSIDL_PROGRAM_FILES_COMMON CSIDL_PROGRAM_FILES_COMMONX86 CSIDL_RECENT CSIDL_RESOURCES CSIDL_RESOURCES_LOCALIZED CSIDL_SENDTO CSIDL_STARTMENU CSIDL_STARTUP CSIDL_SYSTEM CSIDL_SYSTEMX86 CSIDL_TEMPLATES CSIDL_WINDOWS
[edit] Get User's Profile folder
The os module contains a method for expanding a path string to match the current user's profile folder:
>>> import os >>> os.path.expanduser(r'~\myStuff') 'C:\\Users\\joe.himdickel\\myStuff'
The tilde '~' character is where the expanded user profile path is inserted. That method is equivalent to using CSIDL_PROFILE in the Get Special Windows Folder method explained above.
[edit] Get/Set Read-Only File Attribute
Originally posted on Tech Art Tiki. You can do this a couple ways. Using the standard libarary:
import os, stat myFile = r'C:\stuff\grail.txt' fileAtt = os.stat(myFile)[0] if (not fileAtt & stat.S_IWRITE): # File is read-only, so make it writeable os.chmod(myFile, stat.S_IWRITE) else: # File is writeable, so make it read-only os.chmod(myFile, stat.S_IREAD)
Or using the Win32 Extensions:
import win32api, win32con myFile = r'C:\stuff\grail.txt' fileAtt = win32api.GetFileAttributes(myFile) if (fileAtt & win32con.FILE_ATTRIBUTE_READONLY): # File is read-only, so make it writeable win32api.SetFileAttributes(myFile, ~win32con.FILE_ATTRIBUTE_READONLY) else: # File is writeable, so make it read-only win32api.SetFileAttributes(myFile, win32con.FILE_ATTRIBUTE_READONLY)
[edit] Watch a Directory for Changes
A class and working example illustrating how to watch a directory (and optionally subdirs under it) for file changes. Runs in a separate thread, allowing you to check for changes when it's convenient in your script.
import types
import threading
import win32file
import win32con
import os
class WatchDirectory(object):
"""
This method was adapted from example by Tim Golden:
http://tgolden.sc.sabren.com/python/win32_how_do_i/watch_directory_for_changes.html
Watches for changes to any file/dir inside specified directory.
Instantiating this class spawns the watcher function in a separate thread.
self.changeList accumulates changes until client requests list using the check() method.
Calling that method returns the changeList then empties it.
The watching thread will self-terminate when the WatchDirectory instance is destroyed
or falls out-of-scope.
Usage Examples:
watcher = WatchDirectory(r'D:\myStuff')
# Typically you'd have a main loop here somewhere, inside of which you'd make occasional
# calls to watcher.check, like so:
while (not doneWithLoop):
changes = watcher.check()
for c in changes:
# do something to each modified file, etc...
print "file: %s, change: %s" % c
See self.ACTIONS below for a list of possible change strings.
Also, the "onlyExtensions" keyword arg can be used to only watch certain filetypes:
watcher = WatchDirectory(r'D:\myStuff', onlyExtensions=('.jpg', '.xml')
NOTES:
ChangeList returned is chronological, oldest to newest. A file that's modified early in the list
could show up as deleted later. Your client code is responsible for verifying files still
exist, etc.
The win32 method used here seems very reliable. I did notice that it fails to report
deleted files if they were inside a subdir that was deleted in its entirety, however.
Seems like a border case, however, so I'm inclined to leave it as-is for now.
Also, deleted subdirs still sometimes show up as a change with watchSubdirs=False.
TODO:
- Maybe add option to not multithread, making it a blocking call.
Adam Pletcher
adam@volition-inc.com
Volition, Inc./THQ
"""
def __init__(self, dirToWatch, bufferSize=1024, watchSubdirs=True, onlyExtensions=None):
self.dirToWatch = dirToWatch
self.changeList = []
self.bufferSize = bufferSize
self.watchSubdirs = watchSubdirs
if (type(onlyExtensions) == types.StringType):
onlyExtensions = onlyExtensions.split(',')
self.onlyExtensions = onlyExtensions
# Constants for making win32file stuff readable
self.ACTIONS = {
1 : "Created",
2 : "Deleted",
3 : "Modified",
4 : 'Renamed to something',
5 : 'Renamed from "%s"'
}
# Start watching in a separate thread
self.thread = threading.Thread(target=self._checkInThread).start()
def _checkInThread(self):
"""
Watch for changes in specified directory. Designed to be run in a
separate thread, since ReadDirectoryChangesW is a blocking call.
Don't call this directly.
"""
while (1):
FILE_LIST_DIRECTORY = 0x0001
hDir = win32file.CreateFile (
self.dirToWatch,
FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None
)
# The following is a blocking call. Which is why we run this in its own thread.
newChanges = win32file.ReadDirectoryChangesW (
hDir, # Previous handle to directory
self.bufferSize, # Buffer to hold results
self.watchSubdirs, # Watch subdirs?
win32con.FILE_NOTIFY_CHANGE_FILE_NAME | # What to watch for
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY,
None,
None
)
# Changes found, process them
finalChanges = []
oldFilename = None
for change in newChanges:
if (change[0] == 4): # renamed to something
oldFilename = os.path.split(change[1])[1]
pass
else:
file = os.path.join(self.dirToWatch, change[1])
skip = False
# Verify a few things first
if (not self.watchSubdirs) and (os.path.isdir(file)):
skip = True
elif (self.onlyExtensions) and (not os.path.splitext(file)[1] in self.onlyExtensions):
skip = True
if (not skip): # passed checks, so use it
action = self.ACTIONS.get (change[0], "Unknown")
if (change[0] == 5): # renamed from something
# Insert old filename, prior to being renamed
action = action % (oldFilename)
oldFilename = None
# Add change tuple to list
finalChanges.append((file, action))
# Add processed changes to running list
self.changeList += finalChanges
def check(self):
"""
Fetches list of changes our watcher thread has accumulated.
"""
changes = self.changeList
self.changeList = [] # clear changeList
return changes
### MAIN ###
if (__name__ == '__main__'):
import time
watcher = WatchDirectory(r'D:\temp\watchTest')
while (True):
changes = watcher.check()
for c in changes:
print "file: %s, change: %s" % c
time.sleep(5) # wait 5 seconds before checking again
[edit] Primitive Types
Classes implementing basic data types.
[edit] Colour
This code is not production tested and doesn't yet implement all the operator methods (pow etc.) you might need.
Please use it and PM me if you find any bugs/improvements.
class Colour(object):
def __init__(self, r=0.0, g=0.0, b=0.0, a=None, type="float"):
self.type = type
self.r = r
self.g = g
self.b = b
if type == "float":
self.valrange = 1.0
elif type == "byte":
self.valrange = 255
else:
raise AttributeError, "Unknown type for colour class creation"
self.a = a or 1*self.valrange
##########################
## Overloaded built-ins ##
##########################
def __str__(self):
if self.type == "float":
return "Colour(%f, %f, %f, %f, type = %s)" % (self.r, self.g, self.b, self.a, self.type)
else:
return "Colour(%d, %d, %d, %d, type = %s)" % (self.r, self.g, self.b, self.a, self.type)
def __eq__(self, other):
if not isinstance(other, Colour):
return False
return self.r == other.r and self.g == other.g and self.b == other.b and self.a == other.a
def __ne__(self, other):
return not self.__eq__(other)
def __nonzero__(self):
if self.r or self.g or self.b:
return True
return False
def __lt__(self, other):
if not isinstance(other, Colour):
raise TypeError, "Invalid types for comparison"
return self.luminosity() < other.luminosity()
def __le__(self, other):
if not isinstance(other, Colour):
raise TypeError, "Invalid types for comparison"
return (self.luminosity() < other.luminosity()) or self.__eq__(other)
def __gt__(self, other):
if not isinstance(other, Colour):
raise TypeError, "Invalid types for comparison"
return self.luminosity() > other.luminosity()
def __ge__(self, other):
if not isinstance(other, Colour):
raise TypeError, "Invalid types for comparison"
return (self.luminosity() > other.luminosity()) or self.__eq__(other)
def __abs__(self):
newCol = Colour(type=self.type)
newCol.r = abs(self.r)
newCol.g = abs(self.g)
newCol.b = abs(self.b)
newCol.a = abs(self.a)
return newCol
def __add__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
newCol = Colour(type=self.type)
newCol.r = self.r + other.r
newCol.g = self.g + other.g
newCol.b = self.b + other.b
newCol.a = self.a + other.a
return newCol
def __iadd__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
self.r += other.r
self.g += other.g
self.b += other.b
self.a += other.a
return self
def __sub__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
newCol = Colour(type=self.type)
newCol.r = self.r - other.r
newCol.g = self.g - other.g
newCol.b = self.b - other.b
newCol.a = self.a - other.a
return newCol
def __isub__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
self.r -= other.r
self.g -= other.g
self.b -= other.b
self.a -= other.a
return self
def __mul__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
newCol = Colour(type=self.type)
newCol.r = self.r * other.r
newCol.g = self.g * other.g
newCol.b = self.b * other.b
newCol.a = self.a * other.a
return newCol
def __imul__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
self.r *= other.r
self.g *= other.g
self.b *= other.b
self.a *= other.a
return self
def __div__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
newCol = Colour(type=self.type)
newCol.r = self.r / other.r
newCol.g = self.g / other.g
newCol.b = self.b / other.b
newCol.a = self.a / other.a
return newCol
def __idiv__(self, other):
if not isinstance(other, Colour):
other = Colour(r=other, g=other, b=other, a=other, type=self.type)
elif self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
self.r /= other.r
self.g /= other.g
self.b /= other.b
self.a /= other.a
return self
###########
## Casts ##
###########
def toHTML(self):
tempCol = self.toByte()
return "#%02x%02x%02x" % (tempCol.r, tempCol.g, tempCol.b)
def toTuple(self):
return (self.r, self.g, self.b, self.a)
def toList(self):
return [self.r, self.g, self.b, self.a]
def toFloat(self):
newCol = Colour()
newCol.r = self.r
newCol.g = self.g
newCol.b = self.b
newCol.a = self.a
if self.type == "byte":
newCol /= 255.0
newCol.type = "float"
newCol.valrange = 1.0
return newCol
def toByte(self):
newCol = Colour()
newCol.r = self.r
newCol.g = self.g
newCol.b = self.b
newCol.a = self.a
if self.type == "float":
newCol *= 255
newCol.r = int(newCol.r)
newCol.g = int(newCol.g)
newCol.b = int(newCol.b)
newCol.a = int(newCol.a)
newCol.type = "byte"
newCol.valrange = 255
return newCol
#######################
## Utility functions ##
#######################
def _sat_(self, n):
if n < 0:
n = 0
elif n > self.valrange:
n = self.valrange
return n
def saturate(self, affectAlpha=False):
newCol = Colour(a = self.a, type=self.type)
newCol.r = self._sat_(self.r)
newCol.g = self._sat_(self.g)
newCol.b = self._sat_(self.b)
if affectAlpha:
newCol.a = self._sat_(self.a)
return newCol
def invert(self, affectAlpha=False):
return self.__invert__(affectAlpha)
def __invert__(self, affectAlpha=False):
newCol = Colour(a=self.a, type=self.type)
newCol.r = self.valrange - self.r
newCol.g = self.valrange - self.g
newCol.b = self.valrange - self.b
if affectAlpha:
newCol = newCol.invertAlpha()
return newCol
def invertAlpha(self):
newCol = Colour(self.r, self.g, self.b, self.a, self.type)
newCol.a = self.valrange - self.a
return newCol
def luminosity(self):
temp = self.toList()
return (max(temp[:-1]) + min(temp[:-1])) / 2.0
def cheapLuminosity(self):
lumCoeff = Colour(r=0.299,g=0.587,b=0.114)
if self.type == "byte":
lumCoeff = lumCoeff.toByte()
lumCoeff *= self
lumCoeff = lumCoeff.toFloat()
return lumCoeff.r + lumCoeff.g + lumCoeff.b
def lerp(self, other, factor):
if self.type != other.type:
raise ValueError, "Mismatch in operand types \"float\" and \"byte\""
lerped = (other - self) * factor + self
return lerped
[edit] Usage
>>> a = Colour() Colour(0.000000, 0.000000, 0.000000, 1.000000, type = float) >>> b = Colour(1.0, 1.0, 0.5, 1.0) Colour(1.000000, 1.000000, 0.500000, 1.000000, type = float) >>> c = Colour(r=1.0, g=1.0, b=1.0, a=1.0, type="float") Colour(1.000000, 1.000000, 1.000000, 1.000000, type = float) >>> d = Colour(r=127, type="byte") Colour(127, 0, 0, 255, type = byte) >>> a + b Colour(1.000000, 1.000000, 0.500000, 2.000000, type = float) >>> b / 2 Colour(0.500000, 0.500000, 0.250000, 0.500000, type = float) >>> a == b False >>> b < c True >>> a.toByte() Colour(0, 0, 0, 255, type = byte) >>> d.toFloat() Colour(0.498039, 0.000000, 0.000000, 1.000000, type = float) >>> a.lerp(b, 0.4) Colour(0.400000, 0.400000, 0.200000, 1.000000, type = float) >>> (b/3).toHTML() #55552a >>> d.toList() [127, 0.0, 0.0, 255] >>> d.toTuple() (127, 0.0, 0.0, 255) >>> d.invert() Colour(128, 255, 255, 255, type = byte) >>> d.invert(affectAlpha=True) Colour(128, 255, 255, 0, type = byte)
