Fixing main screen problems after reloading python.

Gerikes

User of Run-on Sentences.
Joined
Jul 26, 2005
Messages
1,753
Location
Massachusetts
I don't know if everyone is having this problem, or if it's been well known for awhile. I do know, however, that it really bugs me. At first I thought that it was just a glitch with how python reloading was handled and was out of our reach, but now I realize that it's just the fact that variables are being reset to 0. The problem has to do with some of main screen interface graphics not showing correctly after reloading python modules. The train unit buttons, for example, will no longer show up after I go through the process of reloading the python modules while the game is still running. Everything else, including the new code I made before saving the file, will work fine, but these and a few other buttons are missing. Edit: It appears that if you're in full-screen mode, a few alt-tabs will typically do the trick, eliminating the need for this fix. However, if you run windowed mode, or want to be able to have it load first-time through, then this might be useful.

The problem is with the CvMainInterface.py file. Since this screen is never "remade", the interfaceScreen function is only run once at the very beginning. This is the function that sets many of the global variables. For example, this function sets the global variable g_NumUnitClassInfos to the number of unit classes. This variable in initialized to zero at the top of the file.

When you reload the python files (by saving a python file and then alt-tabbing back to Civ4), the entire file is re-read by the python interpreter, which will in turn run all the commands that aren't part of a function. That includes resetting all of the global variables back to their defaults. So, this would explain why many things wouldn't show up, like unit-train buttons when a city is selected. This is because there is a for-loop that uses the global variable g_NumUnitClassInfos to check if different units are trainable.

Code:
# Units to construct
	for i in range ([b] g_NumUnitClassInfos [/b]):
		eLoopUnit = gc.getCivilizationInfo(pHeadSelectedCity.getCivilizationType()).getCivilizationUnits(i)

		if (pHeadSelectedCity.canTrain(eLoopUnit, False, True)):
			screen.appendMultiListButton( "BottomButtonContainer", gc.getUnitInfo(eLoopUnit).getButton(), iRow, WidgetTypes.WIDGET_TRAIN, i, -1, False )
			screen.show( "BottomButtonContainer" )
						
			if ( not pHeadSelectedCity.canTrain(eLoopUnit, False, False) ):
				screen.disableMultiListButton( "BottomButtonContainer", iRow, iCount, gc.getUnitInfo(eLoopUnit).getButton() )
						
				iCount = iCount + 1
				bFound = True

Because this global variable is reset to zero whenever the python modules are reloaded, the loop will not loop through, and thus why units would not appear. Many items placement in the main interface depend on these global variables, which is why some items would show correctly, and others wouldn't.

In order to fix this, I added the following pieces of code. The idea is to add a global variable that defaults to "True". At the beginning of the updateScreen, it checks if the global variable is True. If so, it will reset the global variables will be reset using a new function. Then, the variable used to check for if a reload happened is set to False so we don't reload the global variables again until the next time the python file is reloaded.

First, near the top of the file, after the global variables are defined(or before, or during, just somewhere outside of a function) , I put:

Code:
# Added by Gerikes for proper python reloading
g_bReset = True
# End Added by Gerikes for proper python reloading

Next, we need to check for whenever this is True.

Code:
# Will update the screen (every 250 MS)
	def updateScreen(self):
		
	[b]# Added by Gerikes to re-interface the screen if we've been reset
	global g_bReset
	if (g_bReset):
		self.resetGlobals()
	# End Added by Gerikes to re-interface the screen if we've been reset[/b]

Finally, we'll need to actually have the self.resetGlobals() function. I put this at the top, of course it doesn't matter where. It doesn't even need to be part of the class.

Code:
class CvMainInterface:
	"Main Interface Screen"

	[b]# Added by Gerikes to properly reset globals
	def resetGlobals(self):
		global g_bReset
		g_bReset = False

		global g_NumEmphasizeInfos
		global g_NumCityTabTypes
		global g_NumHurryInfos
		global g_NumUnitClassInfos
		global g_NumBuildingClassInfos
		global g_NumProjectInfos
		global g_NumProcessInfos
		global g_NumActionInfos

		g_NumEmphasizeInfos = gc.getNumEmphasizeInfos()
		g_NumCityTabTypes = CityTabTypes.NUM_CITYTAB_TYPES
		g_NumHurryInfos = gc.getNumHurryInfos()
		g_NumUnitClassInfos = gc.getNumUnitClassInfos()
		g_NumBuildingClassInfos = gc.getNumBuildingClassInfos()
		g_NumProjectInfos = gc.getNumProjectInfos()
		g_NumProcessInfos = gc.getNumProcessInfos()
		g_NumActionInfos = gc.getNumActionInfos()
	# End Added by Gerikes to properly reset globals[/b]

Hope this helps!
 
Hum, I don't think interfaceScreen is only run once. When I changed some of its code, all I noticed was that you had to tab in and out twice for the new code to run; but then it worked fine.
 
Teg_Navanis said:
Hum, I don't think interfaceScreen is only run once. When I changed some of its code, all I noticed was that you had to tab in and out twice for the new code to run; but then it worked fine.

It wouldn't be python code if tabs weren't somehow messed up, I guess. I tried modifying the tabs so that they looked good in the browser, but since I'm sure people would rather copy and paste, I'll go back and change it.


As for the interfaceScreen, I'm not sure exactly how often it's actually called. My experience with using screens is that interfaceScreen is only called once when the screen is first created, and then the CyGInterfaceScreen object is stored in some out-of-sight data structure to be retrieved even if you don't have the screen variable.
 
I don't mean python tabs, but actually using alt-tab to switch between civ4 and other programs.
 
Teg_Navanis said:
I don't mean the code tabs, but actually using alt-tab to switch between civ4 and other programs.

Ah. Yes, the new code will run normally. This isn't the problem. The problem is that there are some parts on the main screen that get messed up. For example, try alt-tabbing back (you can tell that the code has been reloaded when you get a "Reloading Python Modules..." message in the upper-left) and then opening up the city screen. The problem is that there are now no train, construct, or create buttons. Also, some of the process buttons such as hurry are gone as well. This isn't very good when you're trying to mod what happens when you, say, start training a unit.
 
Gerikes said:
As for the interfaceScreen, I'm not sure exactly how often it's actually called. My experience with using screens is that interfaceScreen is only called once when the screen is first created, and then the CyGInterfaceScreen object is stored in some out-of-sight data structure to be retrieved even if you don't have the screen variable.

I just verified by throwing in a debug statement, interfacescreen() is called only after you alt-tab in the second time after the reload using the vanilla CvMainInterface.py. However, you still usually get a python exception after alt-tabbing in the second time from something else. (Or is it a leftover exception?)
 
You guys were right. If you alt-tab a few times it will eventually work without this code. Unfortunately, the reason I could never get it to work was because it only seems to work when in full-screen mode. When I'm in windowed-mode (which I normally am), alt-tabbing and window-switching will not do the trick, so I still need to use the above method.
 
Gerikes said:
First, near the top of the file, after the global variables are defined(or before, or during, just somewhere outside of a function) , I put:

Code:
# Added by Gerikes for proper python reloading
g_bReset = True
# End Added by Gerikes for proper python reloading

Next, we need to check for whenever this is True.

Code:
# Will update the screen (every 250 MS)
	def updateScreen(self):
		
	[b]# Added by Gerikes to re-interface the screen if we've been reset
	global g_bReset
	if (g_bReset):
		self.resetGlobals()
	# End Added by Gerikes to re-interface the screen if we've been reset[/b]

Finally, we'll need to actually have the self.resetGlobals() function. I put this at the top, of course it doesn't matter where. It doesn't even need to be part of the class.

Code:
class CvMainInterface:
	"Main Interface Screen"

	[b]# Added by Gerikes to properly reset globals
	def resetGlobals(self):
		global g_bReset
		g_bReset = False

		global g_NumEmphasizeInfos
		global g_NumCityTabTypes
		global g_NumHurryInfos
		global g_NumUnitClassInfos
		global g_NumBuildingClassInfos
		global g_NumProjectInfos
		global g_NumProcessInfos
		global g_NumActionInfos

		g_NumEmphasizeInfos = gc.getNumEmphasizeInfos()
		g_NumCityTabTypes = CityTabTypes.NUM_CITYTAB_TYPES
		g_NumHurryInfos = gc.getNumHurryInfos()
		g_NumUnitClassInfos = gc.getNumUnitClassInfos()
		g_NumBuildingClassInfos = gc.getNumBuildingClassInfos()
		g_NumProjectInfos = gc.getNumProjectInfos()
		g_NumProcessInfos = gc.getNumProcessInfos()
		g_NumActionInfos = gc.getNumActionInfos()
	# End Added by Gerikes to properly reset globals[/b]

Hope this helps!

Okay, I've modified this a bit to make it compatible with my (vastly altered) version of CvMainInterface (actually in PLEMainInterface.py).

Instead of creating a resetGlobals function, I simply call interfaceScreen where you call resetGlobals().

So my three changes are:

Code:
# Added by Gerikes for proper python reloading
g_bReset = True
# End Added by Gerikes for proper python reloading

Code:
	# Will update the screen (every 250 MS)
	def updateScreen(self):
		
	[b]# Added by Gerikes to re-interface the screen if we've been reset
	global g_bReset
	if (g_bReset):
		self.interfaceScreen()
	# End Added by Gerikes to re-interface the screen if we've been reset[/b]

Code:
	def interfaceScreen(self):

		[b]# Added by Gerikes for proper python reloading
		global g_bReset
		g_bReset = false
		# End Added by Gerikes for proper python reloading[/b]

This is simpler and can be integrated with most of the mods here, almost regardless of how complicated.

This is not necessary, but I have also added the following debugging statement, but this depends on having PlotListEnhancements in your mod:

Code:
	def interfaceScreen(self):

		# Added by Gerikes for proper python reloading
		global g_bReset
		g_bReset = false

		[b]mt.debug("interfaceScreen() run.")[/b]
		# End Added by Gerikes for proper python reloading

The line should be commented out in your final version. You can use CvUtil.pyPrint() or CyInterface.AddImmediateMessage() if you don't have PlotListEnhancements integrated.

However, there are some funky things that go on when I alt-tab back in the first time after a reload, but no python errors. For example, the tech indicator background does not get drawn and the minimap is screwy. Anyone have any ideas about that? Alt-tabbing in the second time fixes all problems. This won't work for windowed mode. But finding all the global variables and making all the changes required for Gerikes' solution to work with extensive mods to CvMainInterface like PlotListEnhancements will make merging a nightmare in the future.

Edit: Gerikes, perhaps you should request to have this thread moved to Mod Components.
 
Gaurav said:
Okay, I've modified this a bit to make it compatible with my (vastly altered) version of CvMainInterface (actually in PLEMainInterface.py).

Instead of creating a resetGlobals function, I simply call interfaceScreen where you call resetGlobals().

So my three changes are:

Code:
# Added by Gerikes for proper python reloading
g_bReset = True
# End Added by Gerikes for proper python reloading

Code:
	# Will update the screen (every 250 MS)
	def updateScreen(self):
		
	[b]# Added by Gerikes to re-interface the screen if we've been reset
	global g_bReset
	if (g_bReset):
		self.interfaceScreen()
	# End Added by Gerikes to re-interface the screen if we've been reset[/b]

Code:
	def interfaceScreen(self):

		[b]# Added by Gerikes for proper python reloading
		global g_bReset
		g_bReset = false
		# End Added by Gerikes for proper python reloading[/b]

This is simpler and can be integrated with most of the mods here, almost regardless of how complicated.

This is not necessary, but I have also added the following debugging statement, but this depends on having PlotListEnhancements in your mod:

Code:
	def interfaceScreen(self):

		# Added by Gerikes for proper python reloading
		global g_bReset
		g_bReset = false

		[b]mt.debug("interfaceScreen() run.")[/b]
		# End Added by Gerikes for proper python reloading

The line should be commented out in your final version. You can use CvUtil.pyPrint() or CyInterface.AddImmediateMessage() if you don't have PlotListEnhancements integrated.

However, there are some funky things that go on when I alt-tab back in the first time after a reload, but no python errors. For example, the tech indicator background does not get drawn and the minimap is screwy. Anyone have any ideas about that? Alt-tabbing in the second time fixes all problems.

Edit: Gerikes, perhaps you should request to have this thread moved to Mod Components.

I tried at first to simply call interfaceScreen() on the reset, but was having some funky glitches. I guess whatever works is great, but since all the problems with the vanilla CvScreens was with global variables being reset, I thought it safer just to fix those variables. I guess what's important to take away here is just using a global variable, and just finding a way to reset correctly.

Either that, or just go with full-screen. :P
 
Gerikes said:
I tried at first to simply call interfaceScreen() on the reset, but was having some funky glitches. I guess whatever works is great, but since all the problems with the vanilla CvScreens was with global variables being reset, I thought it safer just to fix those variables. I guess what's important to take away here is just using a global variable, and just finding a way to reset correctly.

Either that, or just go with full-screen. :P

I just counted. 49 global variables altered by interfaceScreen() in YAUGM. :eek: Not to mention a lot of instance variables (self.xxx) that probably also have the same issues, and I can't just count global declarations for those. :crazyeye: There were none of those in the vanilla version. No __init__ method creating them. ;) I think I'll stick with my version and fullscreen for now. It's not like I really expect my users to complain. :lol:

Edit: If Firaxis had set it up your way in the first place, I don't think my problem would even exist. All the mods would have followed Firaxis. :sad:
 
Back
Top Bottom