This is my first 'proper' script since learning python and I'd love it if I could get some feedback on things I've done wrong / things I can improve on.  The script takes clipboard data from after effects, containing information on position, rotation and scale.It then gets out the numbers, converts them to keyframe data for The Foundry's Nuke and then writes that data to a text file ready for Nuke to import.As this is my first script I'm sure there is plenty I can streamline / refine.  from itertools import chain, zip_longestfrom PySide import QtGuiimport osimport sys# Fetch data from clipboardapp = QtGui.QApplication(sys.argv)cb = QtGui.QApplication.clipboard()# Needs changing to user input / fetch from cbsrcWidth = 1920srcHeight = 1080# create temporary file for clipboard datawith open('AEClipboardTemp', 'w+') as tempFile:    tempFile.write(cb.text())rotationData = []scaleData = []positionData = []Data Extraction Functionsdef extractRotationData():    keepDataR = False    with open('AEClipboardTemp', 'r') as aeRead:        for line in aeRead:            if line.strip() == Frame\tdegrees:                print(rotation data extracted)                keepDataR = True            elif line.strip() == :                keepDataR = False            elif keepDataR:                rotationData.append(line)def extractScaleData():    keepDataS = False    with open('AEClipboardTemp', 'r') as aeRead:        for line in aeRead:            if line.strip() == Frame\tX percent\tY percent\tZ percent or \                    line.strip() == Frame\tX percent\tY percent:                print(scale data extracted)                keepDataS = True            elif line.strip() == :                keepDataS = False            elif keepDataS:                scaleData.append(line)def extractPositionData():    keepDataP = False    with open('AEClipboardTemp', 'r') as aeRead:        for line in aeRead:            if line.strip() == Frame\tX pixels\tY pixels\tZ pixels or \                    line.strip() == Frame\tX pixels\tY pixels:                print(transform data extracted)                keepDataP = True            elif line.strip() == :                keepDataP = False            elif keepDataP:                positionData.append(line)# Column count for nuke keyframe filenukeCol = 0# Detect if information existswith open('AEClipboardTemp', 'r') as aeRead:    if degrees in aeRead.read().strip().split():        print(rotation data present)        nukeCol += 1        extractRotationData()with open('AEClipboardTemp', 'r') as aeRead:    if percent in aeRead.read().strip().split():        print(scale data present)        nukeCol += 2        extractScaleData()with open('AEClipboardTemp', 'r') as aeRead:    if pixels in aeRead.read().strip().split():        print(transform data present)        nukeCol += 2        extractPositionData()print(Nuke columns = , nukeCol)Remove redundant dataAERData = []AESData = []AEPData = []# remove frame numberfor line in rotationData:    org = line.strip().split()    org = org[1::]    AERData.append(org)# remove frame number and z coordfor line in scaleData:    org = line.strip().split()    with open('AEClipboardTemp', 'r') as aeRead:        for line in aeRead:            if line.strip() == Frame\tX percent\tY percent\tZ percent:                org = org[1:len(org)-1:]                AESData.append(org)            elif line.strip() == Frame\tX percent\tY percent:                org = org[1::]                AESData.append(org)# remove frame number and z coordfor line in positionData:    org = line.strip().split()    with open('AEClipboardTemp', 'r') as aeRead:        for line in aeRead:            if line.strip() == Frame\tX pixels\tY pixels\tZ pixels:                org = org[1:len(org)-1:]                AEPData.append(org)            elif line.strip() == Frame\tX pixels\tY pixels:                org = org[1::]                AEPData.append(org)Conversions from AE to Nuke data# Rotation Data ConvertAERData = [(float(x)*(-1)) for line in AERData for x in line]AERData = [str(line) for line in AERData]nukeRData = [list(x) for x in zip(AERData)]# Scale Data ConvertAESData = [(float(x)/100) for line in AESData for x in line]AESData = [str(line) for line in AESData]nukeSData = [list(x) for x in zip(AESData[0::2], AESData[1::2])]# Position Data Convert# X dataAEPDataX = []AEPDataX.append(list(coord[0::2] for coord in AEPData))AEPDataX = [(float(x)-(srcWidth/2))            for item in AEPDataX for y in item for x in y]AEPDataX = [str(line) for line in AEPDataX]# Y dataAEPDataY = []AEPDataY.append(list(coord[1::2] for coord in AEPData))AEPDataY = [((srcHeight-(float(x)))-(srcHeight/2))            for item in AEPDataY for y in item for x in y]AEPDataY = [str(line) for line in AEPDataY]# Combine X+Y datanukePData = [list(x) for x in zip(AEPDataX, AEPDataY)]# Combine position, rotation and scale datatranslationData = [filter(None, col) for col in                   zip_longest(nukeRData, nukePData, nukeSData)]Format data for nuke ASCII importtData = []for i in translationData:    for item in i:        for x in item:            tData.append(x)nCB = [tData[x:x+nukeCol] for x in range(0, len(tData), nukeCol)]nCBFormat = []for line in nCB:    nCBFormat.append(line)    nCBFormat.append(\n)nCBChain = list(chain.from_iterable(nCBFormat))nCBChain =  .join(nCBChain)Nuke keyframe ASCII File creation functionsdef writeToTemp():    with open(nukeKeysTemp.txt, 'w') as keyFile:        keyFile.write(str(nCBChain))def nukeKeysCreate():    with open(nukeKeysOut_v01.txt, 'w') as writeOut:        with open(nukeKeysTemp.txt, 'r') as wSFix:            for line in wSFix:                cleanLine = line.lstrip()                writeOut.write(cleanLine)def nukeKeysTempDel():    os.remove(nukeKeysTemp.txt)def cleanUp():    try:        os.remove(AEClipboardTemp)    except OSError:        passwriteToTemp()nukeKeysCreate()nukeKeysTempDel()cleanUp()A sample of the clipboard data from After Effects can be found at AE Keyframe Data Example - gistAnd the associated output would be Nuke keyframe file
Python script converts after effects clipboard keyframe data to nuke keyframe data
python;python 3.x;converting
Temporary filesThere is an entire package dedicated to dealing with temporary files. You could improve parts of your code using it.However, it seems mostly unnecessary. The two temporary files you use are:To be able to re-read again and again the same data: use a list of lines instead;To format a single string with newlines in it into a list of lines: do not join using '\n' beforehand.Repeating yourselfAt first sight, your code looks like:Some initialization;3 functions that look alike;3 blocks of code that look alike;2 for-loops that look alike;2 blocks of code that look alike;2 blocks of code that look alike;Some teardown logic.This is a lot of things that look alike. You should try to remove the number of things that look alike to:Not bore potential readers (including yourself);Extract logical blocks that look alike and allow for re-usability of your code;Reduce the burden of maintainability of your code.For that, you will need to parametrize your functions to allow for the same kind of code to be run on different inputs/outputs.For instance, the extractXXXData() could be rewritten:def extractData(output_list, start_condition, data_type_name):    keepData = False    with open('AEClipboardTemp', 'r') as file:        for line in file:            line = line.strip()            if start_condition(line):                print(data_type_name, data extracted)                keepData = True            elif line == :                keepData = False            elif keepData:                output_list.append(line)and used like:nukeCol = 0extractData(rotationData, lambda line: line == Frame\tdegrees, rotation)if rotationData:    nukeCol += 1extractData(scaleData, lambda line: line == Frame\tX percent\tY percent\tZ percent or line == Frame\tX percent\tY percent, scale)if scaleData:    nukeCol += 2extractData(positionData, lambda line: line == Frame\tX pixels\tY pixels\tZ pixels or line == Frame\tX pixels\tY pixels, transform)if positionData:    nukeCol += 2print(Nuke columns = , nukeCol)a lot less code to manage.Your repetitions also lead to havingMost of the code at top-levelAll your variables are global, and all your functions access them globaly. Again, you need to create functions that encapsulate similar behaviours and parametrize them to achieve the specific behaviour you need for a particular dataset.Removing redundant data can be done with:Remove redundant datadef strip_Z_axis_and_Frame(data_in, data_out):    for line in data_in:        # line already stripped in extractData        data_out.append(line.split()[1:3])AERData = []AESData = []AEPData = []strip_Z_axis_and_Frame(rotationData, AERData)strip_Z_axis_and_Frame(scaleData, AESData)strip_Z_axis_and_Frame(positionData, AEPData)Note how you don't even need to know the type of data beforehand here. The slice operator [1:3] will happily drop column 0, take column 1 and take column 2 if it exists. So it will work for rotations too.Same to convert between AE and Nuke data:Conversions from AE to Nuke datadef convert_column(column_data, converting_function):    return [str(converting_function(line)) for line in column_data]AERData = chain.from_iterable(AERData)AESData = chain.from_iterable(AESData)AESDataX = AESData[0::2]AESDataY = AESData[1::2]AEPData = chain.from_iterable(AEPData)AEPDataX = AEPData[0::2]AEPDataY = AEPData[1::2]AERData = convert_column(AERData, lambda x: float(x) * (-1))AESDataX = convert_column(AESDataX, lambda x: float(x) / 100)AESDataY = convert_column(AESDataY, lambda x: float(x) / 100)AEPDataX = convert_column(AEPDataX, lambda x: float(x)-(srcWidth/2))AEPDataY = convert_column(AEPDataY, lambda x: (srcHeight-(float(x)))-(srcHeight/2))Do things one step at a time: extract columns and then convert the data in those columns.Computing the final string is then only a matter of Format data for nuke ASCII importnCB = zip_longest(AERData, AESDataX, AESDataY, AEPDataX, AEPDataY)nCBChain = []for line in nCB:    nCBChain.append(' '.join(filter(None, line)))nCBChain = '\n'.join(nCBChain)Note how I used filter instead of nukeCol to strip out empty columns.Doing that, you can move most of your logic into functions. Most of what is left could be put into a main function that you would call as the (almost) only top-level instruction.About pythonic constructsYou are aware of list-comprehensions, but somehow you mainly use for loops to append data to a list.You are aware of iteration through a list of strings, but somehow you rely on files reading to retrieve the same kind of information several times.You know how to strip data from a line, but somehow you need to use a file to store intermediate string instead of stripping lines in the list before joining them into a single string.While Im at it, let me introduce you to two other functions found in the itertools package:dropwhile: will let you skip data at the beginning of an iterable;takewhile: will let you skip data at the end of an iterable.Combining both, you can simplify the way you search the transformations in your clipboard:def extractData(lines_of_text, searched_text):    searched_text = searched_text.lower()    data_with_headers = takewhile(lambda line: line != '',                                  dropwhile(lambda line: searched_text in line,                                            lines_of_text))    return list(data_with_headers)[2:] # dropping headersThis will require that you store your clipboard in a list of lines (all lowercase); and can be called like:rotationData = extractData(lines_of_text, rotation)scaleData = extractData(lines_of_text, scale)positionData = extractData(lines_of_text, transform)MiscellaneousRandom strings in the middle of the program are weird. Its like dropping a random number in here. It does nothing to the execution flow, but its pure noise. Note that multi-line strings are not multi-line comments. If you want multi-line comments, use # at the beginning of multiple line.Multi-line strings can, however, be found as docstrings, which your code lacks a lot.Constants are better written using UPPER_SNAKE_CASE and kept at the beginning of the file.Functions and variable names are better written using snake_case.It is recommended to put imports from the standard library before other kind of imports.Putting it all togetherimport osimport sysfrom itertools import chain, zip_longest, dropwhile, takewhilefrom PySide import QtGui# Needs changing to user input / fetch from cbWIDTH = 1920HEIGHT = 1080def get_clipboard_lines():    Retrieve the content of the clipboard.    Lowercase and strip it to ease further computation.    Create the QApplication beforehand to be able to get that content.        app = QtGui.QApplication(sys.argv)    return [line.strip().lower()            for line in QtGui.QApplication.clipboard().splitlines()]def create_nuke(filename, data):    Output computed data to the specified file    with open(filename, 'w') as output:        output.writelines(data)#####Data Extraction Functions####def extract_data(lines_of_text, searched_text):    Extract data for frames relevant to a certain type of transformation    searched_text = searched_text.lower()    data_with_headers = takewhile(lambda line: bool(line),                                  dropwhile(lambda line: searched_text in line,                                            lines_of_text))    return list(data_with_headers)[2:] # dropping headersdef strip_frame(data):    Remove the frame number and Z axis from a transformation dataset.    Also flatten the whole data into a single list.        return list(chain.from_iterable(line.split()[1:3] for line in data))def convert_column(column_data, converting_function):    Convert data from AE to Nuke using the supplied function    return [str(converting_function(line)) for line in column_data]def main(lines_of_text, nuke_filename):    Process the content of AE clipboard contained in lines_of_text    and output a Nuke file.        # Column count for nuke keyframe file    rotation = extract_data(lines_of_text, rotation)    scale = extract_data(lines_of_text, scale)    position = extract_data(lines_of_text, transform)    ####    #Remove redundant data    ####    rotation = strip_frame(rotation)    scale = strip_frame(scale)    scale_X = scale[0::2]    scale_Y = scale[1::2]    position = strip_frame(position)    position_X = position[0::2]    position_Y = position[1::2]    ####    #Conversions from AE to Nuke data    ####    frames = zip_longest(        convert_column(rotation, lambda x: float(x) * (-1)),        convert_column(scale_X, lambda x: float(x) / 100),        convert_column(scale_Y, lambda x: float(x) / 100),        convert_column(position_X, lambda x: float(x)-(WIDTH/2)),        convert_column(position_Y, lambda x: (HEIGHT-(float(x)))-(HEIGHT/2))    )    frame_lines = (' '.join(filter(None, line)) for line in frames)    create_nuke(nuke_filename, frame_lines)# Fetch data from clipboardmain(get_clipboard_lines(), nukeKeysOut_v01.txt)Note that I have no idea about the kind of data returned from QtGui.QApplication.clipboard(); I assumed it is a single string. In case it is something else, you can still get it like you did, write it to a temporary file like you did, and return [line.strip().lower() for line in file]; or figure out a proper way to convert it to a list of lines.Ok, enough of that