Python Forum
isolating part of elemnnt in a list
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
isolating part of elemnnt in a list
#1
this is a list

[['1 A'], ['2 B']]

what I want to do is get at the number, add another number to it, and put it back together with the new number value.

I am learning Python on my own, so am just looking for a pointer in the right direction on how to how to pull that number out, as no basic SPLIT seems to account for this type of list setup.

I can provide more info if needed.

Thanks for any info.

moderator: feel free to correct TITLE spelling error of "element" for future search
Reply
#2
Need to get the 0 index of the first list and the 0 index of the inner list to be able to use split on '1 A'.
>>> my_list = [['1 A'], ['2 B']]
>>> a, b = my_list[0][0].split()
>>> my_list[0][0] = '{} {}'.format(int(a)+10, b)
>>> my_list
[['11 A'], ['2 B']]
>>>
Reply
#3
Please, post your best try at the code. Note, that this is list of list, so maybe that is what confuses you..
Reply
#4
seems to me it would be a lot easier to build the list like:
newlist = [[1, 'A'], [2, 'B']]
then you can do your math without hassle, and convert back
to a string only when displaying
Reply
#5
Thank you for the info, I will try it out very shortly but I feel an explanation of the project will help here:

I am learning Pythion because I use Blender and want to make an addon to speed up lip syncing. Here is a sample video of a lip sync test I made last week (audio recorded in my place, I play drums, I make animations): https://youtu.be/DB3MbDHIZOM

I use PapagayoMod1.3 to create the lip sync DAT files. Papagayo can only work with a couple seconds of audio at a time, so I end up with a folder of DAT files. Here is what a DAT file looks like line by line:
MohoSwitch1
1 FV
2 etc
the number is the frame #, the letters are the phoneme mouth shape.

Because I am only able to create a few seconds at a time, each subsequent DAT file would be offset in the animation timeline BUT Papagayo creates each as though it started at frame #1.

So, I need to strip out the first line of each DAT file, change the number on each line to the offset of that particular DAT file's placement on the timeline, write that to a new DAT file, then combine all those DAT files into a single DAT file for the current and capable Blender DAT importer to work. Originally, the plan was to create a new Blender addon, but I then realized the end result of my plan will create a single DAT file, so I will be able to simply use the current DAT importer addon as is. (I will make my own addon for more control at a later date)  As it currently is, I need to import a DAT file, set the parameters, add it to the timeline, rinse, repeat.  I want to have a single DAT file, as it will greatly reduce the time once I am in Blender.

The number that will be added to the frame number for the offset will be taken from the filename (ie: 0234.dat to offset the frames within the file by 234 frames)


This is the Python code I have right now:
#run in working folder

import os, csv

os.makedirs('headerRemoved', exist_ok=True)


#loop through files in directory
for datFilename in os.listdir('.'):
    if not datFilename.endswith('.dat'):
        continue  #skip non DAT files

    baseName = datFilename
    baseName = int(baseName[:-4]) #make string filename an integer


    print ('Removing header from ' + datFilename + '...')


    datRows = []    
    datFileObj = open(datFilename)
    readerObj = csv.reader(datFileObj)
    for row in readerObj:
        if readerObj.line_num == 1:
            continue  #skip first row
        datRows.append(row)
    datFileObj.close()
    

    #write out dat file
    datFileObj = open(os.path.join('headerRemoved', datFilename), 'w', newline='')
    datWriter = csv.writer(datFileObj)
    for row in datRows:
        datWriter.writerow(row)
    datFileObj.close()
And that is where my studies have got me so far!

Okay I now have it changing the first frame # of the DAT files correctly, so that is progress!
#run in working folder

import os, csv

os.makedirs('headerRemoved', exist_ok=True)


#loop through files in directory
for datFilename in os.listdir('.'):
   if not datFilename.endswith('.dat'):
       continue  #skip non DAT files

   baseName = datFilename
   baseName = int(baseName[:-4]) #make string filename an integer


   print ('Removing header from ' + datFilename + '...')


   datRows = []

   datFileObj = open(datFilename)
   readerObj = csv.reader(datFileObj)
   for row in readerObj:
       if readerObj.line_num == 1:
           continue  #skip first row
       datRows.append(row)
   a, b = datRows[0][0].split()
   datRows[0][0] = '{} {}'.format(int(a)+baseName, b)
   datFileObj.close()
   

   #write out dat file
   datFileObj = open(os.path.join('headerRemoved', datFilename), 'w', newline='')
   datWriter = csv.writer(datFileObj)
   for row in datRows:
       datWriter.writerow(row)
   datFileObj.close()
Reply
#6
Could you give an example of what 3 actual DAT files would be named and contain in the format of

0000.DAT
MohoSwitch1
1 FV
2 E
and then what the resulting single DAT file that had the data from the other 3 joined would look like.
Reply
#7
00000.dat:
MohoSwitch1
1 FV
2 L
00234.dat:
MohoSwitch1
1 AI
3 etc
00564.dat:
MohoSwitch1
1 rest
13 E
FINAL.dat:
1 FV
2 L
235 AI
237 etc
565 rest
577 E

The Final.DAT is not correct, as the current DAT importer for Blendet will strip out the first line, so the resulting DAT file needs to be as such:
MohoSwitch1    
1 FV
2 L
235 AI
237 etc
565 rest
577 E

Also let me know how to properly format these code blocks, thanks. What dang button do I click?
Reply
#8
Forum help document > BBCode

This code will create a single file from the .dat files in the folder
import glob

def yield_dat_file(file_name):
    with open(file_name) as file_obj:
        next(file_obj)
        for line in file_obj:
            frame, action = line.split()
            file_number, _ = file_name.split('.')
            yield int(file_number), int(frame), action

def yield_all_dat_files():
    dat_file_names = glob.glob('*.dat')
    for file_name in sorted(dat_file_names):
        for dat_row in yield_dat_file(file_name):
            yield dat_row

def create_single_dat():
    with open('FINAL.dat', 'w') as file_obj:
        file_obj.write('MohoSwitch1\n')
        for file_number, frame, action in yield_all_dat_files():
            file_obj.write('{} {}\n'.format(file_number + frame, action))

create_single_dat()
Final.dat
MohoSwitch1
1 FV
2 L
235 AI
237 etc
565 rest
577 E
Reply
#9
That absolutely does it, creating a DAT file that works perfectly in Blender and leaving the original files as they were. Straight off the top, thank you very much, that is truly amazing and I now see I can eventually create such ideas with speed and accuracy.

I will continue reading the books and learning Python because I only read about the WITH keyword yesterday, so am well behind understanding what you have done, but I will get it figured out, that is not an issue.

For additional fun and info, here is a screenshot of Blender showing the DAT importer GUI on the left of the window with the head (and the test Final.DAT result with accurate timeline plotting below)

[Image: datImport.jpg]

What I want to do with the importer is have it so:

I can select a folder (Current addon only selects a file)
Every DAT in the folder gets the options GUI in a stack for individual control but there is a Global option box with a radio button that turns off individual options if such fine tuning is not needed.
Options can be turned on per DAT file even with Global options controlling remainder.
There is one master PLOT KEYS TO TIMELINE button that iterates the entire stack.
Offset is not needed if filename technique is used, but if not using filename, offset option is required per DAT file.

There is one more option that would be handy but confusing to explain. Here is an attempt. Audacity is what I export the audio clips from. Audacity only displays 24 or 30 frames per second markers. The Papagayo and Blender project may be in a different frame rate, such as 12 frames per second or even 29.97fps. It is fast to note the numbers Audacity displays when exporting the audio clips and skip the little bit of math. Once the DAT is created with Papagayo and then imported into Blender, it would be useful to globally set the Audacity framerate the audio was sourced at, enter the frame # Audacity displayed, and let the Blender addon calculate the actual offset based off the project's fps. This would actually be useful even using the filename technique, as Audacity's frame number could be the filename, allowing for the project's fps to be determined later.

The following is my bastardized version of the Blender addon code, posted simply for completeness of this project's original plan. Hey, it got me studying Python!

Original Importer found here https://wiki.blender.org/index.php/Exten...c_Importer


bl_info = {
    "name": "TREPANING",
    "author": "blazilla",
    "version": (0, 5, 1),
    "blender": (2, 77, 0),
    "location": "3D window > Tool Shelf",
    "description": "Plot Moho (Papagayo dat) file to frames",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Import-Export"}


import bpy, re, os
from bpy.props import *
from bpy.props import IntProperty, FloatProperty, StringProperty

global lastPhoneme
lastPhoneme="nothing"

datFile = os.listdir('C:\\dat')



# -------------------------------------------------------------------------------

# reading imported file & creating keys
def lipsyncer():
    
    obj = bpy.context.object
    scn = bpy.context.scene


    f = open(scn.fpath) # importing file
    f.readline() # reading the 1st line that we don"t need
    
    for line in f:

        # removing new lines
        lsta = re.split("\n+", line)

        # building a list of frames & shapes indexes
        lst = re.split(":? ", lsta[0])# making a list of a frame & number 
        frame = int(lst[0])
        
        for key in obj.data.shape_keys.key_blocks:
            if lst[1] == key.name:
                createShapekey(key.name, frame)


# creating keys with offset and eases for a phonem @ the frame

def createShapekey(phoneme, frame):
    
    global lastPhoneme
    
    scn = bpy.context.scene
    obj = bpy.context.object
    objSK = obj.data.shape_keys
    
    offst = scn.offset     # offset value
    skVlu = scn.skscale    # shape key value
    
    #in case of Papagayo format
    if scn.regMenuTypes.enumFileTypes == '0' :
        frmIn = scn.easeIn     # ease in value
        frmOut = scn.easeOut   # ease out value
        hldIn = scn.holdGap    # holding time value
        

    # inserting the In key only when phonem change or when blinking
    if lastPhoneme!=phoneme or eval(scn.regMenuTypes.enumModeTypes) == 1:
        objSK.key_blocks[phoneme].value=0.0
        objSK.key_blocks[phoneme].keyframe_insert("value",
            -1, offst+frame-frmIn, "Lipsync")
            
    objSK.key_blocks[phoneme].value=skVlu
    objSK.key_blocks[phoneme].keyframe_insert("value", 
        -1, offst+frame, "Lipsync")
    
    objSK.key_blocks[phoneme].value=skVlu
    objSK.key_blocks[phoneme].keyframe_insert("value", 
        -1, offst+frame+hldIn, "Lipsync")
            
    objSK.key_blocks[phoneme].value=0.0
    objSK.key_blocks[phoneme].keyframe_insert("value", 
    -1, offst+frame+hldIn+frmOut, "Lipsync")
    
    lastPhoneme = phoneme
 

# lipsyncer operation start
class btn_lipsyncer(bpy.types.Operator):
    bl_idname = 'lipsync.go'
    bl_label = 'Start Processing'
    bl_description = 'Plots the voice file keys to timeline'

    def execute(self, context):

        scn = context.scene
        obj = context.active_object

        # testing if object is valid
        if obj!=None:
            if obj.type=="MESH":
                if obj.data.shape_keys!=None:
                    if scn.fpath!='': lipsyncer()
                    else: print ("select a Moho file")
                else: print("No shape keys")
            else: print ("Object is not a mesh")
        else: print ("Select object")
        return {'FINISHED'}




#defining custom enumeratos
class menuTypes(bpy.types.PropertyGroup):

    enumFileTypes = EnumProperty(items =(('0', 'Papagayo', ''), 
                                         ),
                                 name = 'Choose FileType',
                                 default = '0')


    enumModeTypes = EnumProperty(items =(('0', 'Lipsyncer',''),
                      ),
                                 name = 'The Mode is',
                                 default = '0')

           
# drawing the user interface


class LipSyncUI(bpy.types.Panel):
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOL_PROPS"
    bl_label = "Dat TREPANING LipSync Importer"
    
    newType= bpy.types.Scene
    
    newType.fpath = StringProperty(name="Import File ", description="Select your voice file", subtype="FILE_PATH")
    newType.skscale = FloatProperty(description="Smoothing shape key values", min=0.1, max=1.0, default=0.8)
    newType.offset = IntProperty(description="Offset your frames", default=0)

    newType.easeIn = IntProperty(description="Smoothing In curve", min=1, default=2)
    newType.easeOut = IntProperty(description="Smoothing Out curve", min=1, default=2)
    newType.holdGap = IntProperty(description="Holding for slow keys", min=0, default=3)


    def draw(self, context):
        
        obj = bpy.context.active_object
        scn = bpy.context.scene
        

        layout = self.layout
        col = layout.column()

        # showing the current object type
        if obj != None:
            if obj.type == "MESH":
                split = col.split(align=True)
                split.label(text="The active object is: ", icon="OBJECT_DATA")
                split.label(obj.name, icon="EDITMODE_HLT")
            else:
                col.label(text="The active object is not a Mesh!", icon="OBJECT_DATA")
        else:
            layout.label(text="No object is selected", icon="OBJECT_DATA")
            
        col.row().prop(scn.regMenuTypes, 'enumModeTypes')
        col.separator()
        
        # the lipsyncer panel 
        if scn.regMenuTypes.enumModeTypes == '0':


            # Papagayo panel


            col.prop(context.scene, "fpath")

            split = col.split(align=True)
            split.label("Frame Offset :")
            split.prop(context.scene, "offset")

                #split = col.split(align=True)
               # split.label("Key Value :")
               # split.prop(context.scene, "skscale")

            split = col.split(align=True)
            split.prop(context.scene, "easeIn", "Ease In")
            split.prop(context.scene, "holdGap", "Hold Gap")
            split.prop(context.scene, "easeOut", "Ease Out")
                
            col.operator('lipsync.go', text='Plot Keys to the Timeline') 

        
# clearing vars
def clear_properties():

    # can happen on reload
    if bpy.context.scene is None:
        return
     
    props = ["fpath", "skscale", "offset", "easeIn", "easeOut", "holdGap"]
    for p in props:
        if p in bpy.types.Scene.bl_rna.properties:
            exec("del bpy.types.Scene."+p)
        if p in bpy.context.scene:
            del bpy.context.scene[p]



# registering the script
def register():
    bpy.utils.register_module(__name__)
    bpy.types.Scene.regMenuTypes = PointerProperty(type = menuTypes)

def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.context.scene.regMenuTypes

    clear_properties()

if __name__ == "__main__":
    register()
Reply
#10
After a bit more thought, I realize the Blender addon project could be simplified by keeping the addon as is but have the selection choose a folder of DAT files, then have the addon run the code to create the Final.DAT from all the numbered DAT files in the folder, and, as a not required bonus, place that Final.DAT file into the selection field. Everything else could stay exactly as it is.

A quick test shows the current addon can select a folder and fill the selection field with that folder path, even with no file selected. So, a separate button could be added that will call the Final.DAT creation code, using the folder path in the selection field as the working folder for the code. Then, the selection field can be used again to select that Final.DAT, or one could type Final.DAT into the selection field, or the code could append the folder path with the filename 'Final.DAT'.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Adding List Element if Second part of the List Elements are the Same quest_ 3 2,467 Nov-25-2020, 04:33 PM
Last Post: bowlofred
  Select a part of an element of a list with Index BollerwagenIng 0 1,857 Aug-09-2019, 09:27 AM
Last Post: BollerwagenIng

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020