Noob + addPanel() = problem ?

Negator_UK

King
Joined
Nov 14, 2007
Messages
758
I'm trying to develop a little information mod, no python experience, but I've done OK so far pulling some code from jeckel & baldyr to make some small progress.

I am trying to build a menu out of a vertical of stack panels, similar to the info screen in Civ4 BTS.

I am using addPanel() but I have a problem.

I can create a background panel, or a single foreground panel, but not multiple panels.

Is there a rule against this ? there seems to be multiple addPanel() calls in the screens in the main game python code, but I am befuddled.

help ! (see comments in code, about 3/4 down).

Code:
#
# SMUG Mod AI Information Screen.
# By Negator, with help from Jeckel
#
#

from CvPythonExtensions import *
import CvScreenEnums

############################################################################
# globals

gc = CyGlobalContext()
ArtFileMgr = CyArtFileMgr()
localText = CyTranslator()

class CvAIInfoScreen:

	def __init__(self):
		self.sWidgetPrefix = "smugAIInfoScreen"

		self.MANAGER_SCREEN_ID = self.sWidgetPrefix + "MainWindow"
		self.BACKGROUND_ID = self.sWidgetPrefix + "BackgroundImage"
		self.EXIT_ID = self.sWidgetPrefix + "ExitWidget"

        # private class data - needs to match background bitmap

		self.X_SCREEN = 0
		self.Y_SCREEN = 0
		self.W_SCREEN = 1024
		self.H_SCREEN = 512

        #############################################################################################################

	def getScreen(self):
		return CyGInterfaceScreen(self.MANAGER_SCREEN_ID, CvScreenEnums.SMUG_AI_INFO_SCREEN)

	def hideScreen(self):
		screen = self.getScreen()
		screen.hideScreen()
		return None

	def update(self, fDelta):
		return None

	# handle the input for this screen...
	def handleInput(self, inputClass):
		screen = self.getScreen()
		iNotifyCode = inputClass.getNotifyCode()
		iNotifyClicked = NotifyCode.NOTIFY_CLICKED
		iNotifyChar = NotifyCode.NOTIFY_CHARACTER

		if(iNotifyCode == iNotifyClicked):
                        sInputName = inputClass.getFunctionName()
		return 0

        #############################################################################################################

	# Screen construction function
	def interfaceScreen(self):

		screen = self.getScreen()
		if (screen.isActive()):
			return None

		# Find out our resolution

		xResolution = screen.getXResolution()
		yResolution = screen.getYResolution()

		# set screen size

		x0 = (xResolution-self.W_SCREEN) / 2
		y0 = (yResolution-self.H_SCREEN) / 2;
		screen.setDimensions(x0, y0, self.W_SCREEN, self.H_SCREEN)

		# set background

		myTitle = "AI Info Screen"
		myHelp  = "Get your AI info here !"
		isVertical = 0
		isScrollable = 1

		# Draw a big red rectangle as background to menu.
		# This works.

		myP = "Bkg"
		x = 0
		y = 0
		w = self.W_SCREEN
		h = self.H_SCREEN
		myStyle = PanelStyles.PANEL_STYLE_BLUE50

		#screen.addPanel(myP, myTitle, myHelp, isVertical, isScrollable, x, y, w, h, myStyle)
		#screen.setPanelColor (myP, 150, 0, 0)
		#screen.Hide(myP)

		# Draw a vertical stack of smaller rectangles in the big one
		# This does not work, although the first one will draw OK if
		# I comment out the big red one above - wtf ?

		mainBorder  = 10

		y = 40
		x = x + mainBorder
		w = w - mainBorder
		h = 40
		isScrollable = 0
		myStyle = PanelStyles.PANEL_STYLE_BLUE50

		i = 1

		while i < 5:
			myTag = "x" * i
			myP = "panel" + myTag
			myTitle = "AI No " + myTag
			myHelp = "Help for AI No " + myTag
			screen.addPanel(myP, myTitle, myHelp, isVertical, isScrollable, x, y, w, h, myStyle)
			screen.setPanelColor (myP, 0, 0, 100)
			screen.Hide(myP)
			y = y + 100
			i = i + 1

		# End of problem code (I think)

		# set render info

		screen.setRenderInterfaceOnly(True);
		screen.showScreen(PopupStates.POPUPSTATE_IMMEDIATE, False)
		screen.showWindowBackground(True)

        #############################################################################################################
 
hhmm...i made some experiences with that in another not civ related project, so i'm not sure if it applies here, but it could be.
The program has probably a problem with the more than once used variable name.
You're using myP as a variable name in a loop with always a new definition, but the game will just always add myP again, not seeing the difference, and will so either just override the previous definition, or will ignore the afterwards following ones. Whatever the case it, at the end the game has added one panel, named myP.

Might sound dull, but changing screen.addPanel(myP to screen.addPanel("panel"+"x" * i should change something (but i'm not sure how the game is here multiplying a string with an int in your code, but i don't know how python handles it, never tried it).
----

It makes at all also some sense, if you think about it. If the screen is present, then you can click on it and maybe cause an action. And how would you do that, if you can't for sure say where it's coming from, because the item itself is not more present, because another one has replaced it?
 
I'm using Varanese's "Game Scripting Mastery" as my python book and it has an example that multiplies "!" by 8 to give "!!!!!!!!", so that part of the syntax should work, but this is my first python project so who knows.

It does give the correct help text and title for the small panel in the where case I comment out the creation of the big red background, so that much is working.

It was my intention to use myP as a variable name because I want to use it in a loop. Initially I had the whole mile-long line for my addPanel() command and replaced all the parameters with short auto-variables (like in C?) to make it more readable. It still seemed to work for a single panel for all the other parameters.

It is my intention to add things to these smaller panels so I'll need their names again eventually, but I suppose an expression will do as a descriptor.

I'll play around with it tomorrow along the lines you suggest.

If the screen is present, then you can click on it and maybe cause an action. And how would you do that, if you can't for sure say where it's coming from, because the item itself is not more present, because another one has replaced it?

I haven't got as far as user input yet, apart from using f10 to activate the menu like Jeckels Skeleton project did - I'm sure I'll have questions about that later :D
 
It should maybe be mentioned that i am also a relative noob and a really bad programmer, so i'll probably not of much help.


mmhh...are you sure that you're creating the smaller panels into the right direction? Normally we assume that 0,0 is the left upper edge, and that the positive axes are going to the right and to the bottom, but that depends on the framework imho. Can also be, that the axis to the bottom is the negative one....wait...you said it worked before without a loop, right? Damn.
Are you sure that the visible one is the first panel, or is it maybe the last panel?
 
You cannot put a scrollable container within another scrollable container.

Now, you have the first one with scrolling enabled, and the second one disabled. HOWEVER, I believe that isn't enough, simply because the potential exists for the second one to be capable to have been assigned scrollability.


So use the other type of Panel which doesn't have a bScrollable parameter and it should work.
 
Might sound dull, but changing screen.addPanel(myP to screen.addPanel("panel"+"x" * i should change something

Nope, no change there.

You cannot put a scrollable container within another scrollable container.

That makes sense, but it doesn't explain why I only get the first panel of my vertical stack (when I comment out the background), there should be 4. I try different spacings to make sure I'm not overlapping them , but no luck so far.

I tried addLineGFC() but I get nothing with that either, but I'm still feeling my way around.

Any ideas what other command I can use instead of addPanel() ?
 
I've selected a new thing to draw but it still doesn't work.

I have tried to copy the exit button from CvDomesticAdvisor.py but I still cant get it working - it doesn't work when I comment out the background rectangle either ? any ideas ?

Code:
#
# SMUG Mod AI Information Screen.
# By Negator, with help from Jeckel
#
#

from CvPythonExtensions import *
import CvScreenEnums

############################################################################
# globals

gc = CyGlobalContext()
ArtFileMgr = CyArtFileMgr()
localText = CyTranslator()

class CvAIInfoScreen:

	def __init__(self):
		self.sWidgetPrefix = "smugAIInfoScreen"

		self.MANAGER_SCREEN_ID = self.sWidgetPrefix + "MainWindow"
		self.BACKGROUND_ID = self.sWidgetPrefix + "BackgroundImage"
		self.EXIT_ID = self.sWidgetPrefix + "ExitWidget"

        # private class data - needs to match background bitmap

		self.X_SCREEN = 0
		self.Y_SCREEN = 0
		self.W_SCREEN = 1024
		self.H_SCREEN = 512

        #############################################################################################################

	def getScreen(self):
		return CyGInterfaceScreen(self.MANAGER_SCREEN_ID, CvScreenEnums.SMUG_AI_INFO_SCREEN)

	def hideScreen(self):
		screen = self.getScreen()
		screen.hideScreen()
		return None

	def update(self, fDelta):
		return None

	# handle the input for this screen...
	def handleInput(self, inputClass):
		screen = self.getScreen()
		iNotifyCode = inputClass.getNotifyCode()
		iNotifyClicked = NotifyCode.NOTIFY_CLICKED
		iNotifyChar = NotifyCode.NOTIFY_CHARACTER

		if(iNotifyCode == iNotifyClicked):
                        sInputName = inputClass.getFunctionName()
		return 0

        #############################################################################################################

	# Screen construction function
	def interfaceScreen(self):

		screen = self.getScreen()
		if (screen.isActive()):
			return None

		# Find out our resolution

		xResolution = screen.getXResolution()
		yResolution = screen.getYResolution()

		# set screen size

		screen.setRenderInterfaceOnly(False)

		x0 = (xResolution-self.W_SCREEN) / 2
		y0 = (yResolution-self.H_SCREEN) / 2;
		screen.setDimensions(x0, y0, self.W_SCREEN, self.H_SCREEN)

		screen.showScreen(PopupStates.POPUPSTATE_IMMEDIATE, False)

		# set background

		myTitle = "AI Info Screen"
		myHelp  = "Get your AI info here !"
		isVertical = 0
		isScrollable = 1

		# Draw a big rectangle as background to menu.
		# This works.

		x = 0
		y = 0
		w = self.W_SCREEN
		h = self.H_SCREEN
		myStyle = PanelStyles.PANEL_STYLE_MAIN

		screen.addPanel("smugAiInfoBG", myTitle, myHelp, isVertical, isScrollable, x, y, w, h, myStyle)
		screen.setPanelColor ("smugAiInfoBG", 150, 0, 0)
		screen.Hide("smugAiInfoBG")

		# Draw Exit Button - copied from Domestic advisor screen in main game, doesn't work - Help !
		#screen.setText("DomesticExit", "Background", localText.getText("TXT_KEY_PEDIA_SCREEN_EXIT", ()).upper(), CvUtil.FONT_RIGHT_JUSTIFY, self.nScreenWidth - 25, self.nScreenHeight - 45, -0.1, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_CLOSE_SCREEN, -1, -1 )

		screen.setText("smugAiInfoExit", "smugAiInfoBG", "Exit", CvUtil.FONT_RIGHT_JUSTIFY, 200, 300, -0.1, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_CLOSE_SCREEN, -1, -1 )
		#screen.setText("smugAiInfoExit", "Background", "Exit", CvUtil.FONT_RIGHT_JUSTIFY, 200, 300, -0.1, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_CLOSE_SCREEN, -1, -1 )
		#screen.moveToFront("smugAiInfoExit")

		# set render info

		screen.setRenderInterfaceOnly(True);
		screen.showScreen(PopupStates.POPUPSTATE_IMMEDIATE, False)
		screen.showWindowBackground(True)

        #############################################################################################################
 
Sadly I still haven't recovered from losing my harddrive a while back, so can't give actual code chunks from my own work. The closest I had come to this (where I learned the scrollable limitation) was in changing the Civics screen for Fall Further to have more details and yet speedy swapping capability.

This did require multiple panels stacked one on top of the other, and vertical screen scrollability when there were too many panels to fit on the screen. As well as the top-most panel being horizontally scrollable if too many options exist. So it might have some worthwhile snippets for you.

But unfortunately getting the code requires downloading all of Fall Further (or possibly RifE, I think they carried the civics screen over). I thought that I had shared the code modification back to the original writers of the scrollable civics screen, but it seems that once I got things to work properly with the second scrollable container I jumped straight to defining new Widgets as well, which made the code useless without my DLL.

I believe that this post has Johnny Smith's approach, which at least has stacked panels whic scroll vertically. So might help you.
 
Ok, Thanks.

I've started to have some better success with that code xienwolf posted, will post some progress soon.

One thing that puzzles me is the code snippet below - they look like two looping re-assignments that sets each variable with the last value through each loop - is this a bug, or is there some mysterious python-tech I am missing.

Code:
		for k in range(gc.getNumCivicOptionInfos()):
			self.CIVICCATEGORIES = k
		for l in range(gc.getNumCivicInfos()):
			self.HEADINGS_HEIGHT = (((40 + self.BUTTON_SIZE + self.TEXT_MARGIN) * l/self.CIVICCATEGORIES)/5 * 2) + 30
 
Ok, I've made some progress, going with a table at the moment.

Here's what I get:

Spoiler :


Here's how I get it:

Code:
#
# Sid Meier's Civilization 4
# Copyright Firaxis Games 2005

# SMUG mod by Negator


from CvPythonExtensions import *
import CvUtil
import ScreenInput
import CvScreenEnums
import string
import CvScreensInterface

# globals
gc = CyGlobalContext()
ArtFileMgr = CyArtFileMgr()
localText = CyTranslator()

class CvAIInfoScreen:

	def __init__(self):

		self.PREFIX    = "smugAiInfo"
		self.ID_SCREEN = self.PREFIX + "MainWindow"
		self.ID_BG     = self.PREFIX + "BG"
		self.ID_TABLE  = self.PREFIX + "Table"
		self.ID_EXIT   = self.PREFIX + "Exit"

		self.currentFunc = "notset"

		#############################################################
		# A GUI system with no RECT ? and I paid money for this ?
		# Ouch !
		# Use those Python Tuple thingys as rect structures rather
		# than clutter things up with a new rect class now...
		# r[0] = x 
		# r[1] = y
		# r[2] = width
		# r[3] = height
		# Maybe I should look into named tuple thingys later
		#############################################################

		# I really need to figure out how to get users screen res.
		# Use my own personal favourite for now for r_screen

		self.r_screen = (0,0,1280,768)

		# The overall size of the new screen,
		# background will be drawn over whole screen area 

		self.xborder = 40
		self.yborder = 80

		self.r_back   = (self.r_screen[0] + self.xborder,
					     self.r_screen[1] + self.yborder,
					     self.r_screen[2] - (2*self.xborder),
					     self.r_screen[3] - (2*self.yborder))

		# Table for AI Information

		self.xtborder = 16	# sides
		self.ttborder = 40	# top
		self.btborder = 40	# bottom

		self.r_table = (self.r_back[0] + self.xtborder,
						self.r_back[1] + self.ttborder,
						self.r_back[2] - (2*self.xtborder),
						self.r_back[3] - self.ttborder - self.btborder)

		self.table_columns = 11

		# Exit button location

		self.x_exit = self.r_back[0] + self.r_back[2] - 80
		self.y_exit = self.r_back[1] + self.r_back[3] - 38

		# generic z plane info

		self.z_text = -8.1	# this seems to work with or without the minus sign

		return

	def setCurrentFunc(self, txt):
		# Used for Debugging

		self.currentFunc = txt
		return

	def logLocal(self, txt):
		# Used for debugging

		print (self.currentFunc, " : ", txt)
		return

	def getScreen(self):
		return CyGInterfaceScreen(self.ID_SCREEN, CvScreenEnums.SMUG_AI_INFO_SCREEN)

	def drawBackground(self, screen):

		r = self.r_back
		print("drawBackground()", r)
		screen.addPanel(self.ID_BG, u"", "Heeeelp!!", True, True, r[0], r[1], r[2], r[3], PanelStyles.PANEL_STYLE_MAIN)

		return

	def drawTable(self, screen):

		r = self.r_table
		print("drawTable()", r)

		screen.addTableControlGFC( self.ID_TABLE, self.table_columns,
								   r[0], r[1], r[2], r[3],
								   True, True, 24, 24,
								   TableStyles.TABLE_STYLE_STANDARD )

		screen.enableSelect( self.ID_TABLE, True )
		screen.enableSort( self.ID_TABLE )

		# This has no effect :(

		screen.enableGridlines (self.ID_TABLE, True, True)

		# This line seems redundant where I copied it from
		#screen.setStyle("CityListBackground", "Table_StandardCiv_Style")

		return

	def drawExitButton(self, screen):

		screen.setText(self.ID_EXIT, self.ID_BG,
					   "Byee", CvUtil.FONT_LEFT_JUSTIFY,
					   self.x_exit, self.y_exit, self.z_text,
					   FontTypes.TITLE_FONT, WidgetTypes.WIDGET_CLOSE_SCREEN, -1, -1)
		return

	def interfaceScreen (self):

		# Set our debugging headings in the log
		# & check to see if we are already showing our screen

		self.setCurrentFunc("CvAIInfoScreen.interfaceScreen()")
		self.logLocal("Called")

		screen = self.getScreen()
		if screen.isActive():
			return

		self.logLocal("Drawing")

		# Render Away !

		screen.setRenderInterfaceOnly(True);
		screen.showScreen( PopupStates.POPUPSTATE_IMMEDIATE, False)
		screen.setDimensions(self.r_screen[0], self.r_screen[1], self.r_screen[2], self.r_screen[3])

		self.drawBackground(screen)
		self.drawTable(screen)
		self.drawExitButton(screen)

		# You can comment this line out and it still works  - ???

		screen.showWindowBackground(False)

		# OK, we're done

		self.setCurrentFunc("CvAIInfoScreen.interfaceScreen()")
		self.logLocal("Drawing Done")

		# Bye

		return 0

Here's the Debugging output

Spoiler :
('CvAIInfoScreen.interfaceScreen()', ' : ', 'Called')

11000 SCREEN TURNED ON

('CvAIInfoScreen.interfaceScreen()', ' : ', 'Drawing')

('drawBackground()', (40, 80, 1200, 608))

('drawTable()', (56, 120, 1168, 528))

('CvAIInfoScreen.interfaceScreen()', ' : ', 'Drawing Done')


There are some questions in the code for those who know.

My next step is to fill the table, I'll be trying to rip code from the domestic advisor screen to do this.

A couple of questions in addition to those above.

1. How do I print to the in-game message line (the one with "autosaving.." in the pic).
2. Every time I make a change to the code I restart the game - is this necessary ? or can I reload the module somehow ?

Cheers.
 

Attachments

  • Civ4ScreenShot0016.JPG
    Civ4ScreenShot0016.JPG
    52 KB · Views: 194
1)
PHP:
CyInterface().addMessage(iPlayerID,False,15,CyTranslator().getText("TXT_PUT_SOME_TEXT_HERE",()),'',0,'Art/Interface/Put/Path/To/Button/Here.dds',ColorTypes(44), iXCoordinate, iYCoordinate, True,True)

Easier:
PHP:
CyInterface().addImmediateMessage("Some text here","")

2) If you edit the .py files during the game, and save it, the game should automatically reload the module. This does not always work flawlessly (okay, in the minority of the cases). Some problems get solved by simply reloading a save afterwards, but often you really have to close + restart it.


No clue about the real questions :blush:.
 
Don't worry, there are always more questions :D

But first some progress.

Spoiler :


Its been a struggle, but the first part of the mod works on a minimal basis.

You can see on the left I used the city icon from the domestic advisor (who also 'advised' me on my coding for this :D), instead of anAI leader portrait - any advice on how I can do this using game assets ?

I also need to figure out the relationship bewteen the noWarProb variables in the middle (labelled annoyed->friendly) and the DecWar variables at the end. I'll probably have to post this question on the S&T forum.
 

Attachments

  • Civ4ScreenShot0017.JPG
    Civ4ScreenShot0017.JPG
    80.2 KB · Views: 270
Top Bottom