Indirection on components?

Good day all. I have this python code:

line = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65]
quad = ["A","B","C","D"]
	
for i in line:
    for n in quad:
        TWQuadOpName = system.tag.readBlocking("[default]Twisters/TW"+str(i)+"/Operator Assignments/Quad_"+str(n)+" J Shift Operator Name")[0].value
        TWQuadOpBadgeNum = system.tag.readBlocking("[default]Twisters/TW"+str(i)+"/Operator Assignments/Quad_"+str(n)+" J Shift Operator BadgeNum")[0].value	

The tag read works great, I am curious if I could use that same read function have conditions that only write values into certain containers if they have only certain contents. Hopefully this shows what im wanting to do, but in a much more condensed format:

###### CoordinateContainer has TW1 - 12 ######
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_A_OpName = TWQuadOpName
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_A_OpNum = TWQuadOpBadgeNum
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_B_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_B_OpNum
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_C_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_C_OpNum
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_D_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW1").props.params.Quad_D_OpNum
	
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_A_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_A_OpNum
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_B_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_B_OpNum
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_C_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_C_OpNum
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_D_OpName
	self.parent.getChild("CoordinateContainer").getChild("TW2").props.params.Quad_D_OpNum
	
	###### CoordinateContainer_0 has TW13 - 24 ######
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_A_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_A_OpNum
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_B_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_B_OpNum
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_C_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_C_OpNum
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_D_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_D_OpNum
		
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_A_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_A_OpNum
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_B_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_B_OpNum
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_C_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_C_OpNum
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_D_OpName
	self.parent.getChild("CoordinateContainer_0").getChild("TW14").props.params.Quad_D_OpNum

So, you can see that CoordinateContainer has TW1 - 12 and 1-12 will have the same 8 parms and will continue for CoordinateContainer_0 which will have TW13-24, CoordinateContainer_1 which will have TW25-40, CoordinateContainer_2 which will have TW41-56 and CoordinateContainer_3 which will have TW57-65. Those same 8 parms will be in each TW.

How can I make this happen?

Two pieces of advice:

  1. Batch your tag reads. Pass all your tag paths at once to system.tag.readBlocking, or you're killing your performance.
  2. You can read parameters by string easily, e.g. these are equivalent:
    self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params.Quad_A_OpName
    self.parent.getChild("CoordinateContainer_0").getChild("TW13").props.params['Quad_A_OpName']
    Which you should be able to use to assemble a string path to read as long as you're okay making the assumption that your UI elements will have the correct name all the time.
3 Likes

Alright, so Ive been playing around and came up with something like this:

	system.tag.writeBlocking("[default]Twisters/Selected Shift From Assigner",'K')
		
line = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65]
quad = ["A","B","C","D"]
		
for i in line:
    for n in quad:
        TWQuadOpName = system.tag.readBlocking("[default]Twisters/TW"+str(i)+"/Operator Assignments/Quad_"+str(n)+" J Shift Operator Name")[0].value
        TWQuadOpBadgeNum = system.tag.readBlocking("[default]Twisters/TW"+str(i)+"/Operator Assignments/Quad_"+str(n)+" J Shift Operator BadgeNum")[0].value	
		
tw_ranges = [(1, 12), (13, 24), (25, 40), (41, 56), (57, 65)]
	
for cc_index, (tw_start, tw_end) in enumerate(tw_ranges):
    for tw_num in range(tw_start, tw_end + 1):
        for n in quad:
            self.parent.getChild("CoordinateContainer_""+str(cc_index)+").getChild("TW"+str(tw_num)+"").props.params.Quad_A_OpName = TWQuadOpName
            self.parent.getChild("CoordinateContainer_""+str(cc_index)+").getChild("TW"+str(tw_num)+"").props.params.Quad_A_OpNum = TWQuadOpBadgeNum

Any advice? @PGriffith I will look at Batch reading

I will work on an optimized version of this, but I would be really surprised if this code actually executes.

This:

self.parent.getChild("CoordinateContainer_""+str(cc_index)+")

Is not valid python. I think you mean

self.parent.getChild("CoordinateContainer_" + str(cc_index))
2 Likes

This is the same as line = range(1, 66) by the way.

1 Like

that would be super helpful sir, thank you

And you can reduce the number of tag reads to one.

tags = []
for i in range(1, 66):
    for n in quad:
        tags.append("[default]Twisters/TW"+str(i)+"/Operator Assignments/Quad_"+str(n)+" J Shift Operator Name")
        tags.append("[default]Twisters/TW"+str(i)+"/Operator Assignments/Quad_"+str(n)+" J Shift Operator BadgeNum")
system.tag.readBlocking(tags)
1 Like

I suggest this

from itertools import product, chain

line = range(66)
quad = list("ABCD")

paths = list(chain.from_iterable(
    [
        "[default]Twisters/TW{}/Operator Assignments/Quad_{} J Shift Operator Name".format(l, q),
        "[default]Twisters/TW{}/Operator Assignments/Quad_{} J Shift Operator BadgeNum".format(l, q)
    ] for l, q in product(line, quad)
))

values = [qval.value for qval in system.tag.readBlocking(paths)]

Probably don't need that list() even, since strings are iterable.

And if you really want to golf it, you could have ("Name", "BadgeNum") as another element in the product() call.

1 Like

I want to use the indirection on the left side of the = sign. I am trying to do it like this but am getting an error:

tw_ranges = [(1, 12), (13, 24), (25, 40), (41, 56), (57, 65)]
quad = ["A", "B", "C", "D"]
        
for cc_index, (tw_start, tw_end) in enumerate(tw_ranges):
    for tw_num in range(tw_start, tw_end + 1):
        for n in quad:
        	self.parent.getChild("CoordinateContainer_{}").getChild("TW{}").props.params.Quad_{}_OpName.format(cc_index, tw_num, n) = TWQuadOpName
	        self.parent.getChild("CoordinateContainer_{}").getChild("TW{}").props.params.Quad_{}_OpNum.format(cc_index, tw_num, n) = TWQuadOpBadgeNum

the error comes in this area
image

Right, because that's not a valid identifier.
You need to be creating a string, and then passing that string within square brackets, like I mentioned in my first post:

self.parent.getChild("CoordinateContainer_" + cc_index).getChild("TW" + tw_num).props.params['Quad_{}_OpName'.format(n)] = TWQuadOpName

Because at each . you're accessing a different object.

2 Likes

Sorry @PGriffith I didnt notice that. apologies. I have this now:

def runAction(self, event):
	system.tag.writeBlocking("[default]Twisters/Selected Shift From Assigner",'J')
	
	line = range(1,65)
	quad = ["A", "B", "C", "D"]
   			
	for i in line:
	    for n in quad:
	        TWQuadOpName = system.tag.readBlocking("[default]Twisters/TW" + str(i) + "/Operator Assignments/Quad_" + str(n) + " J Shift Operator Name")[0].value
	        TWQuadOpBadgeNum = system.tag.readBlocking("[default]Twisters/TW" + str(i) + "/Operator Assignments/Quad_" + str(n) + " J Shift Operator BadgeNum")[0].value
	   			
		tw_ranges = [(1, 12), (13, 24), (25, 40), (41, 56), (57, 65)]
		quad = ["A", "B", "C", "D"]
			        
		for cc_index, (tw_start, tw_end) in enumerate(tw_ranges):
		    for tw_num in range(tw_start, tw_end + 1):
		        for n in quad:
		        	self.parent.getChild("CoordinateContainer_" + cc_index).getChild("TW" + tw_num).props.params['Quad_{}_OpName'.format(n)] = TWQuadOpName
			        self.parent.getChild("CoordinateContainer_" + cc_index).getChild("TW" + tw_num).props.params['Quad_{}_OpNum'.format(n)] = TWQuadOpName

does this look ok?

You're still issuing a bunch of sequential calls to system.tag.readBlocking. Every single one of those is a major performance penalty, and you're doing them in series, rather than parallel.

2 Likes

Okay, so I happen to have a couple of functions that I use for this type of thing.

The first one will take an itterable and return multiple iterables of the given size.

def chunks(it,size):
    from itertools import izip_longest as zipl
    return zipl(*[iter(it)] * size)

So chunks(['A','B','C','D'],2) would return ('A','B'),('C','D')

The second basically does what @pascal.fragnoud showed except it takes in a number of times to repeat the group.

def rangeGroup(it,start,n):
    from itertools import repeat
    from itertools.chain import from_iterable as chainIt

   for i,j in zip(chainIt(repeat(k,len(it)) for k in xrange(start,n)),chainIt(repeat(it,n))):
        yield (i,j)

Using these I have done as much as I could to reduce the number of iterations through lists, I also only make one call to system.tag.readBlocking()

quad = ['A','B','C','D']
path = '[default]Twisters/TW{}/Operator Assignments/Quad_{} {}'
tags = ['J Shift Operator Name','J Shift Operator BadgeNum']

paths = [path.format(i,n,tag) for i,n in rangeGroup(quad,0,66) for tag in tags]

tagValues = [qv.value for qv in system.tag.readBlocking(paths)]

#I incremented the end value here rather than in the loop.
tw_ranges = [(1,13),(13,25),(25,41),(41,57),(57,66)]

for cc_index,(tw_start,tw_end) in enumerate(tw_ranges):
    for tw_num,quad in rangeGroup(quad,tw_start,tw_end):
       cont = 'CoordinateContainer_{}'.format(cc_index)
       tw = 'TW{}'.format(tw_num)
       nameQuad = 'Quad_{}_OpName'.format(quad)
       numQud = 'Quad_{}_OpNum'.format(quad)

       for opName,opNum in chunks(tagValues,2):
           self.parent.getChild(cont).getChild(tw).props.params[nameQuad] = opName
           self.parent.getChile(cont).getChild(tw).props.params[numQuad] = opNum
2 Likes

You're right, I was trying kevin's 'half-assing' methodology.

from itertools import product, chain

line = range(3)
quad = "AB"
tags = ["Name", "BadgeNum"]
paths = list(
    chain(
        "[default]Twisters/TW{}/Operator Assignments/Quad_{} J Shift Operator {}".format(l, q, t)
        for l, q, t in product(line, quad, tags)
    )
)

values = [qval.value for qval in system.tag.readBlocking(paths)]

Or, to push the golf mode to where it shouldn't go:

values = [
    qval.value for qval in system.tag.readBlocking(
        list(
            chain(
                "[default]Twisters/TW{}/Operator Assignments/Quad_{} J Shift Operator {}".format(l, q, t)
                for l, q, t in product(range(66), "ABCD", {"Name", "BadgeNum"})
            )
        )
    )
]
2 Likes

Hey! That's looking like some of my Sim Aids V2 examples!

2 Likes

Well it actually doesn't look half as bad as I imagined before typing it.
It's mainly the list(chain()) induced nesting that makes me twitch a little. Could probably be made more readable by putting them on the same level, or removing list() if readBlocking can take a generator (but I don't think it can).
And maybe by extracting the constant part of the tag path.

I still have to try Sim Aid :X

Would the structure look like this:

def runAction(self, event):
def chunks(it,size):
	from itertools import izip_longest as zipl
	return zipl(*[iter(it)] * size)
def rangeGroup(it,start,n):
	from itertools import repeat
	from itertools.chain import from_iterable as chainIt
	
	   for i,j in zip(chainIt(repeat(k,len(quad)) for k in xrange(start,n)),chainIt(repeat(quad,n))):
	        yield (i,j)
	        
	        quad = ['A','B','C','D']
	        path = '[default]Twisters/TW{}/Operator Assignments/Quad_{} {}'
	        tags = ['J Shift Operator Name','J Shift Operator BadgeNum']
	        
	        paths = [path.format(i,n,tag) for i,n in rangeGroup(quad,0,66) for tag in tags]
	        
	        tagValues = [qv.value for qv in system.tag.readBlocking(paths)]
	        
	        #I incremented the end value here rather than in the loop.
	        tw_ranges = [(1,13),(13,25),(25,41),(41,57),(57,66)]
	        
	        for cc_index,(tw_start,tw_end) in enumerate(tw_ranges):
	            for tw_num,quad in rangeGroup(quad,tw_start,tw_end):
	               cont = 'CoordinateContainer_{}'.format(cc_index)
	               tw = 'TW{}'.format(tw_num)
	               nameQuad = 'Quad_{}_OpName'.format(quad)
	               numQuad = 'Quad_{}_OpNum'.format(quad)
	        
	               for opName,opNum in chunks(tagValues,2):
	                   self.parent.getChild(cont).getChild(tw).props.params[nameQuad] = opName
	                   self.parent.getChile(cont).getChild(tw).props.params[numQuad] = opNum

Props to you for using itertools, thanks for helping spread the good word !
But please don't import IN the functions :X

2 Likes

Concur. Those belong at the top level of a project library script. An import inside a function is a "code smell".