How to get # of values returned in a loop

phungus420

Deity
Joined
Mar 1, 2003
Messages
6,296
I tried googling this, and now have a basic understanding of the for loop, something which confused me before. So that's all good, but I couldn't find the answer to my question.

Say I have an unknown word stored in a variable called szWORD. So I set up a loop like:

Code:
for letter in szWORD
	#number of letters in szWORD
How do I determine how many letters are in the variable?

How this relates to civ, is I'm trying to get this code to work:
Code:
	def placeCiv(self):
		screen = self.top.getScreen()
		for iCiv in range(gc.getNumCivilizationInfos()):
			civ = gc.getCivilizationInfo(iCiv)
			if civ.isLeaders(self.iLeader):
				#if multiple civs
					panelName = self.top.getNextWidgetName()
					screen.addPanel( panelName, localText.getText("TXT_KEY_PEDIA_CATEGORY_CIV", ()), "", False, True, self.X_CIVS, self.Y_CIVS, self.W_CIVS, self.H_CIVS, PanelStyles.PANEL_STYLE_BLUE50 )
					screen.attachLabel(panelName, "", "  ")
					screen.attachImageButton(panelName, "", civ.getButton(), GenericButtonSizes.BUTTON_SIZE_CUSTOM, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, 1, False)
				#else
					screen.setImageButton(self.top.getNextWidgetName(), civ.getButton(), self.X_CIV, self.Y_CIV, self.W_CIV, self.H_CIV, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, 1)
But I can't find anything with google telling me how to get a count of how many values were returned in the loop. In this case I just want an if > 1 returned values, but I'm also just interested in general how to do this with a for loop.
 
Generally the cheapo way to do what you want is a counting variable
Code:
iCount = 0
for "something":
  iCount = iCount + 1
  "Do stuff"
print iCount

For your case, you probably want to use a list. During your loop over all civilizations, you want to just store any civ for which isLeaders() returns true in a list. Then at the end (outside of the loop) you run your IF/ELSE statement, based on the size of the list. Or if you cannot easily get the size of a list in python (I don't know python so well, just C++), run a count variable as well.
 
Edit, nevermind got it.

This was an important part, BTW:
Xienwolf said:
then at the end (outside of the loop)

Anyway this code works. It looks pretty messy and overwritten though. Anyone see anyway to simplify it:
Code:
	def placeCiv(self):
		screen = self.top.getScreen()
		civLeaderList = []
		for iCiv in range(gc.getNumCivilizationInfos()):
			civ = gc.getCivilizationInfo(iCiv)
			if civ.isLeaders(self.iLeader):
				civLeaderList.append(civ)
				civCount = len(civLeaderList)
		if (civCount > 1):
			panelName = self.top.getNextWidgetName()
			screen.addPanel( panelName, localText.getText("TXT_KEY_PEDIA_CATEGORY_CIV", ()), "", False, True, self.X_CIVS, self.Y_CIVS, self.W_CIVS, self.H_CIVS, PanelStyles.PANEL_STYLE_BLUE50 )
			screen.attachLabel(panelName, "", "  ")
			for iCiv in range(gc.getNumCivilizationInfos()):
				civ = gc.getCivilizationInfo(iCiv)
				if civ.isLeaders(self.iLeader):
					screen.attachImageButton(panelName, "", civ.getButton(), GenericButtonSizes.BUTTON_SIZE_CUSTOM, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, 1, False)
		else:
			for iCiv in range(gc.getNumCivilizationInfos()):
				civ = gc.getCivilizationInfo(iCiv)
				if civ.isLeaders(self.iLeader):
					screen.setImageButton(self.top.getNextWidgetName(), civ.getButton(), self.X_CIV, self.Y_CIV, self.W_CIV, self.H_CIV, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, 1)
 
If you want to get really fancy and take advantage of the power of Python, you can merge the two if/else blocks into a single block by using functions defined within the placeCiv() function:

Code:
	def placeCiv(self):
		screen = self.top.getScreen()
		civLeaderList = []
		for iCiv in range(gc.getNumCivilizationInfos()):
			civ = gc.getCivilizationInfo(iCiv)
			if (civ.isLeaders(self.iLeader)):
				civLeaderList.append(civ)
		if (len(civLeaderList) > 1):
			panelName = self.top.getNextWidgetName()
			screen.addPanel( panelName, localText.getText("TXT_KEY_PEDIA_CATEGORY_CIV", ()), "", False, True, self.X_CIVS, self.Y_CIVS, self.W_CIVS, self.H_CIVS, PanelStyles.PANEL_STYLE_BLUE50 )
			screen.attachLabel(panelName, "", "  ")
			def placeCivIcon(civ):
				screen.setImageButton(panelName, "", civ.getButton(), 
						GenericButtonSizes.BUTTON_SIZE_CUSTOM, 
						WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, civ.getID(), 1, False)
		else:
			def placeCivIcon(civ):
				screen.setImageButton(self.top.getNextWidgetName(), civ.getButton(), 
						self.X_CIV, self.Y_CIV, self.W_CIV, self.H_CIV, 
						WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, civ.getID(), 1)
		for civ in civLeaderList:
			placeCivIcon(civ)

What i do is define a placeCivIcon() function whose definition is chosen by the length of the list. In Python scoping is at the function level. The function defined inside the if() test remains valid until the end of the function containing it. So at the end placeCivIcon(civ) calls whichever function was defined above it in the if- or else-block.

I <3 Python.

I also changed a few other things:

  • Calculate the length of the list outside the loop so you do it only once at the end
  • Place the CvCivInfos into the list and iterate over that list so you only iterate over all civilizations once. The second iteration is over only the ones you found above.
 
Thanks EF. I was thinking it made no sense to run the exact same loop twice, especially since I had already stored it in a list. That code accomplishes exactly what I intuitively was thinking should be done, I just didn't know how to put those thoughts into code.
 
Bah, just found a bug in that code EF. When you click the civ button that is placed in the leaderscreen, it always redirects to the Zulu. The code I have is slightly different then yours, because yours throws an exception due to the fact civ.getID doesn't work. So here is the current code:

Code:
	def placeCiv(self):
		screen = self.top.getScreen()
		civLeaderList = []
		for iCiv in range(gc.getNumCivilizationInfos()):
			civ = gc.getCivilizationInfo(iCiv)
			if (civ.isLeaders(self.iLeader)):
				civLeaderList.append(civ)
		if (len(civLeaderList) > 1):
			panelName = self.top.getNextWidgetName()
			screen.addPanel( panelName, localText.getText("TXT_KEY_PEDIA_CATEGORY_CIV", ()), "", False, True, self.X_CIVS, self.Y_CIVS, self.W_CIVS, self.H_CIVS, PanelStyles.PANEL_STYLE_BLUE50 )
			screen.attachLabel(panelName, "", "  ")
			def placeCivIcon(civ):
				screen.attachImageButton(panelName, "", civ.getButton(), GenericButtonSizes.BUTTON_SIZE_CUSTOM, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, 1, False)
		else:
			def placeCivIcon(civ):
				screen.setImageButton(self.top.getNextWidgetName(), civ.getButton(), self.X_CIV, self.Y_CIV, self.W_CIV, self.H_CIV, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, 1)
		for civ in civLeaderList:
			placeCivIcon(civ)


Also the code in post #3 does not cause this bug. It works normally, so something in the optimization from post 3's code to the above is causing this bug.
 
You cannot use iCiv because iCiv is always going to be the last value used in the for loop (the Zulu). Bah, CvInfoBase doesn't have the objects ID from getInfoTypeForString(). That's uber-dumb. Here's the fixed code:

Code:
	def placeCiv(self):
		screen = self.top.getScreen()
		civLeaderList = []
		for iCiv in range(gc.getNumCivilizationInfos()):
			civ = gc.getCivilizationInfo(iCiv)
			if (civ.isLeaders(self.iLeader)):
				civLeaderList.append((iCiv, civ))
		if (len(civLeaderList) > 1):
			panelName = self.top.getNextWidgetName()
			screen.addPanel( panelName, localText.getText("TXT_KEY_PEDIA_CATEGORY_CIV", ()), "", False, True, self.X_CIVS, self.Y_CIVS, self.W_CIVS, self.H_CIVS, PanelStyles.PANEL_STYLE_BLUE50 )
			screen.attachLabel(panelName, "", "  ")
			def placeCivIcon(iCiv, civ):
				screen.setImageButton(panelName, "", civ.getButton(), 
						GenericButtonSizes.BUTTON_SIZE_CUSTOM, 
						WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, -1, False)
		else:
			def placeCivIcon(iCiv, civ):
				screen.setImageButton(self.top.getNextWidgetName(), civ.getButton(), 
						self.X_CIV, self.Y_CIV, self.W_CIV, self.H_CIV, 
						WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIV, iCiv, -1)
		for iCiv, civ in civLeaderList:
			placeCivIcon(iCiv, civ)
 
Thanks EF, with a minor correction that code works like a charm. I tried to get the CivID, but couldn't figure out how to do it looking through the PythonAPI, glad to know I'm not an idiot and that the call just didn't exist.

Just for my understanding, could you clarify more what this loop is doing?
Code:
for iCiv, civ in civLeaderList:
			placeCivIcon(iCiv, civ)
 
In the for() loop at the top, for each civilization the leader can lead I add a pair of values to the list as a single object called a tuple. A tuple is just like a list except that it cannot be modified. After the loop the list is a list of (ID, CvCivilizationInfo) tuples.

The bottom for() loop iterates over all of those tuple objects. In Python you can break apart a tuple/list into separate variables by having a comma on the left:

Code:
x, y = (2, 3)
iCiv, civ = (3, gc.getCivilizationInfo(3))
a, b, c = ("a", "b", "c")

You can combine that in a for() loop to iterate over a list containing tuples/lists and break them apart into variables. It's most handy when dealing with dictionaries which contain key/value pairs:

Code:
d = {
    1: "hello",
    2: "world",
    3: "!",
}
for k, v in d:
    print k, v

[I]> 1 hello
> 2 world
> 3 ![/I]
 
OK, thanks.
 
Back
Top Bottom