For the curious (includes source code snippets)

Hi!

After causing a bit of commotion with my programming style, I figured that sharing my source code thus far could help people understand how I approach my programming project and why I felt the way I felt about certain things.

However, currently, what source code I have programmed is not really enough to warrant an upload (I don't want to waste people's interest by making something as big and not delivering. Therefore, I will include the bits in this post (cutting out a few redundant comments, maybe).

So, if you don't want to see a Python newbie's awkward code for fear of getting confused, I suggest you don't read any further.

Since I didn't know the theme yet (starting about 6 hours before now without having internet access (^_^;; ), I solely concentrated on technical stuff, so there is no content at all yet.

Directory structure

Currently, with no readme.txt or so written, there is only one file in the root directory, namely main.py (okay, a very original name). In addition, the root directory currently has one subdirectory called code where all the rest of the code resides.

Currently, code contains application.py, constants.py, globals.py, mainmenu.py, and mainwindow.py.

main.py

(Header comment removed because layout crunched by HTML)

# This program was written for Python 2.5.1 in combination with
# wxPython 2.8.4.

# Initializing external libraries

try:
___ import sys
except:
___ print "Importing the standard library 'sys' has failed!\nPlease check the integrety of your Python installation."
___ raise

#
# try to import sys
#

try:
___ import math
except:
___ print "Importing the standard library 'math' has failed!\nPlease check the integrity of your Python installation."
___ raise

#
# try to import math
#

try:
___ a = sys.version_info
except:
___ print "You need version 2.5 or later of Python in order to run this program!"
___ raise

#
# fetch the version number of Python
#

if (a[0] == 2) and (a[1] < 5):
___ print "You need version 2.5 or later of Python in order to run this program!"
___ sys.exit()

#
# check whether Python is at least version 2.5
# Note: version_info was not available in 1.x versions of Python, which is why
# a[0] == 2 is sufficient.
#

try:
___ import wx
except:
___ print "Importing the wxPython libraries has failed!\nYou need to have the wxPython libraries (version 2.8.4 or later) installed in order to run this program."
___ raise

#
# try to import wx, which is the current method of importing the wxPython libraries
#
a = wx.version()

if (a[0] < '2') or ((a[0] == '2') and ((a[2] < '8') or ((a[2] == '8') and (a[2] < '4')))):
___ print "You need at least version 2.8.4 of the wxPython libraries in order to run this program!"
___ sys.exit()

#
# Check the version number of wxPython.
# Version 2.8.4 is required.
#


#
# Verifying top level
# -------------------
# This module is meant to be run by itself and will not execute as
# an imported library!

if (__name__ != "__main__"):
___ print "This module was not designed to be used as an imported module.\nPlease start it by itself!"
___ sys.exit()
#
# Abort program execution if this module isn't boss around here.
#

#
# Initializing original code
# --------------------------
# Note: In order to avoid conflicts with nested try ... except calls
# the following 'execfile' commands are not backed up by try except

execfile("code/constants.py")

#
# Initialize global constants
#

execfile("code/globals.py")

#
# Initialize global variables
#

execfile("code/mainmenu.py")

#
# Initialize the class for the main menu
#

execfile("code/mainwindow.py")

#
# Initialize the class for the main window
#

execfile("code/application.py")

#
# Initialize the application class
#

#
# Starting the Application
#
# ----------------------------
# Note: This 'module' is not meant to be imported at all. That is
# why I am not including the compatibility if statement.
DW_Application = DW_Class_Application(redirect = False)
DW_Application.MainLoop()

#
# What a big main program :)
# An instance of the my application class is created.
# Redirect = False causes error messages to go to Python's window.
# and then I start the standard main loop included in the basic application # class
#

constants.py

(Again comments shortened)
# Constants declaration
#
# This file initializes most of the 'constants' the game uses.
# Please note that these are not real constants, technically
# speaking and some may later be initialized from a data file.
# They are constants in the sense that they are not to be changed
# during the main program execution. Any changes to them are to be
# considered equivalent of restarting the program.
#
# Constants are marked by the 'Const' prefix in the name (right
# after the 'DW' prefix :) :) :) ).
#
# Screen and Display Constants

DW_Const_Screen_Width = 640
DW_Const_Screen_Height = 480

#
# Screen width and height; just as you would expect :)
# Used in application window initialization and various
# graphical calculations (probably (^_^;; )
#

#
# Map Mode and Tile Constants
#

DW_Const_Tile_Width = 32
DW_Const_Tile_Height = 32

#
# Tile width and height; again, quite obvious
# Used by routines handling map display, but also in combination
# with any display of items that are eligible for map display (e.g. character sprite)
#

DW_Const_Map_Display_Tile_Width = 15
DW_Const_Map_Display_Tile_Height = 13

#
# Height and width of the map display measured in tiles
# Used by map displaying routines
#

DW_Const_Map_Display_X_Margin = int(DW_Const_Map_Display_Tile_Width / 2)
DW_Const_Map_Display_Y_Margin = int(DW_Const_Map_Display_Tile_Height / 2)

#
# Derived constants used by the map displaying routines in many functions
# concerning map centering. Measured in tiles.
#

DW_Const_Max_Map_Width = 200
DW_Const_Max_Map_Height = 200

#
# Maximum size for any maps (measured in tiles).
# Needed by map loading routines to check for legitimate map size and #

#
# Miscellaneous Tool Constants
#

DW_Const_Hex_to_Dec = {"0" : 0, "1" : 1, "2" : 2, "3" : 3, "4" : 4,
______________________ "5" : 5, "6" : 6, "7" : 7, "8" : 8, "9" : 9,
______________________ "a" : 10, "b" : 11, "c" : 12, "d" : 13, "e" : 14,
______________________ "f" : 15, "A" : 10, "B" : 11, "C" : 12, "D" : 13,
______________________ "E" : 14, "F" : 15}
DW_Const_Dec_to_Hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
______________________ "a", "b", "c", "d", "e", "f"]

#
# These two constants are used if on the spot conversion from and to
# hexadecimal strings is needed.
# Note that Hex to Dec is designed to respond correctly to upper and lower
# case of 'a' to 'f'.
#

globals.py

(Header comment largely abbreviated, similar to constants)
# Globals declaration
#
# This file, which is also a top level initialization file, creates
# the global variables and sets them to default values.
# Global variables are variables that need to be accessed and
# often (but not always) changed by routines during the main program
# execution.
# Note: This file also contains certain option variables which would
# usually be changed via an option menu. Due to time constraints,
# the implementation of such a menu seems unlikely
#
# Globals are marked by the 'Global' prefix in the name (right
# after the 'DW' prefix, of course :) :) :) ).
#
# Map Variables
#

DW_Global_Basic_Map = []
x = 0
while (x < DW_Const_Max_Map_Width):
___ Helper = []
___ y = 0
___ while (y < DW_Const_Max_Map_Height):
_______ Helper.append("00")
_______ y = y + 1
___ DW_Global_Basic_Map.append(Helper)
___ x = x + 1

#
# The basic map data, provided as hex strings in a [x][y] array
#

#
# Options
#

DW_Global_Key_Delay = 500

#
# This global determines how many milliseconds input is being ignored
# after successful processing
#

mainmenu.py

(Comment abbrev. as usual) # Main Menu Class
#
# Currently, this will be used as a test file.
# Eventually, it will contain all routines relevant to the Main
# Menu of the game.
#
# Class definitions are marked by 'Class' following the 'DW' prefix.

class DW_Class_Main_Menu:
___ def __init__(self, DW_Window):
_______ self.DW_Parent = DW_Window
_______ self.DW_Current_Selection = 0
_______ self.DW_Max_Selection = 4
___ def DW_Up(self):
_______ if (self.DW_Current_Selection > 0):
___________ self.DW_Current_Selection = self.DW_Current_Selection - 1
_______ return True
___ def DW_Down(self):
_______ if (self.DW_Current_Selection < self.DW_Max_Selection):
___________ self.DW_Current_Selection = self.DW_Current_Selection + 1
_______ return True
___ def DW_Left(self):
_______ return False
___ def DW_Right(self):
_______ return False
___ def DW_Confirm(self):
_______ return False
___ def DW_Cancel(self):
_______ sys.exit()

mainwindow.py

(...)
# Main Window Class Definition
#
# This file contains the definition of the main window class which
# has only one instance which is created later within the single
# instance of the personalized Application class.
# Besides providing the basic output location, it links the input
# caught by the application to the routines in its children - well,
# actually, they just pass through, so this class is more like a
# container (^_^;;
#
# Class definitions are marked by 'Class' following the 'DW' prefix.

class DW_Class_Main_Window(wx.Frame):
___ def __init__(self): _______ wx.Frame.__init__(self, None, -1, "RPG")
_______ self.SetClientSizeWH(DW_Const_Screen_Width, DW_Const_Screen_Height)
_______ self.DW_Main_Panel = wx.Panel(self, -1)
_______ self.DW_Buffer = wx.MemoryDC()
_______ self.DW_Active = DW_Class_Main_Menu(self)
# self.Show()
_______ self.ShowFullScreen(True)

application.py

(...)
# Application Class Definition
#
# This file contains the definition of the application class for
# program. Programs (including this one) usually have only one
# instance of an application class.
# Besides being the container for the main window, the application
# will also be responsible for handling most input. Except for
# special cases like entry boxes, all input events will be
# here before being sent to the handling routines.
#
# Class definitions are marked by 'Class' following the 'DW' prefix.

class DW_Class_Application(wx.App):
___ def OnInit(self):
_______ """This routine effectively initializes the application."""
_______ self.DW_Main_Window = DW_Class_Main_Window()
# The main window is created
_______ self.SetTopWindow(self.DW_Main_Window)
# and explicitly defined as the top window
_______ self.Bind(wx.EVT_KEY_DOWN,self.OnKeyDown)
# Keyboard events are linked to the application's OnKeyDown event
_______ self.DW_Key_Lock = False # DW_Key_Lock states whether keyboard input is to be accepted.
# While it is true, no keyboard input will be handled by DW_Class_Application.
_______ return(True)

___ def DW_Unlock_Keys(self):
_______ self.DW_Key_Lock = False
# This tiny functions is called by a timer whenever there is a key lock
# The only thing it does is resetting DW_Key_Lock, thus lifting the key lock.

___ def OnKeyDown(self, DW_wx_event):
_______ """Handles mapped keyboard input for the application"""
_______ if (self.DW_Key_Lock):
___________ return
# If DW_Key_Lock is set, any input is ignored
_______ DW_Lock_Request = False
# The local variable DW_Lock_Request is used to store whether or not
# the input should be locked.
_______ DW_Code = DW_wx_event.GetKeyCode()
# The keycode is retrieved and
_______ if (DW_Code == wx.WXK_ESCAPE) or (DW_Code == wx.WXK_SPACE):
___________ DW_Lock_Request = self.DW_Main_Window.DW_Active.DW_Cancel()
_______ elif (DW_Code == wx.WXK_LEFT) or (DW_Code == wx.WXK_NUMPAD4):
___________ DW_Lock_Request = self.DW_Main_Window.DW_Active.DW_Left()
_______ elif (DW_Code == wx.WXK_RIGHT) or (DW_Code == wx.WXK_NUMPAD6):
___________ DW_Lock_Request = self.DW_Main_Window.DW_Active.DW_Right()
_______ elif (DW_Code == wx.WXK_DOWN) or (DW_Code == wx.WXK_NUMPAD2):
___________ DW_Lock_Request = self.DW_Main_Window.DW_Active.DW_Down()
_______ elif (DW_Code == wx.WXK_UP) or (DW_Code == wx.WXK_NUMPAD8):
___________ DW_Lock_Request = self.DW_Main_Window.DW_Active.DW_Up()
_______ elif (DW_Code == wx.WXK_RETURN) or (DW_Code == wx.WXK_NUMPAD5):
___________ DW_Lock_Request = self.DW_Main_Window.DW_Active.DW_Confirm()
# ... compared to various cases resulting in calls to the appropriate
# subroutines within the DW_Active object in the main window.
# This way, keyboard mapping happens only once, namely here, while
# each phase of the application can have its own behavior based on the
# commands mapped.
# If the called function wants to lock the keyboard, it should return
# True, otherwise False
_______ if (DW_Lock_Request):
___________ self.DW_Key_Lock = True
___________ wx.CallLater(DW_Global_Key_Delay,self.DW_Unlock_Keys)
# Finally, should there be a lock request, the keys are locked (that is
# a flag is set to cause this method to abort prematurely and a timer
# is set to call an unlocking method after a specified amount of
# milliseconds has passed (that value is stored in the global variable
# DW_Global_Key_Delay.

End of Code

Well, this is how far I got that far. Not really big, I know, but I am still optimistic. And putting it up here has cleared my head a bit. And maybe now it has become clearer why I like execfile (^_^;;

Deathworks

(log in to comment)

Comments

Just a quickie: put your code fragments inside a <pre> tag and then you don't need all those underscores. Sorry, I meant to mention this earlier but forgot! :(

Sorry, but I still fail to see why you can't just have the modules that need it "import constants" and then refer to constants.<varname> ... your globals and constants all have that DW_XXXX_ prefix, which is about the same length as a module name anyway...

Also, for hex string to integer conversion, try int('0123456789ABCDEF', 16) and for decimal to hex try '%x'%1234567890 (or the hex() builtin, but it adds the '0x' prefix which you may not want).

Finally, I strongly recommend that you use the Skellington (see the email I sent out or the help page) since this is your first time. It has a bunch of useful stuff in it. Not using it, and leaving out some things, can result in snarky comments from judges :)

Hi!

Ah! That <pre> would have been useful indeed (^_^;;

Anyhow, hopefully, the next time I am online, I will be able to upload something that is worth being uploaded and copy-pasted.

Well, I have to admit that I find this thing about changing namespaces with the modules a little bit daunting. And the zen of Python did mention something about rather flat than nested :) :) :)

I figured there were some routines, but since it is easily implemented (for my purposes) using these two constants, I figured it would be best to use something I understand and where I definitely know what the result looks like (the '0x' prefix, for example, could have caused me some trouble). Anyhow, in most cases, it will be single digit or double-digit hex numbers that need conversion, four-digit would probably be extremely rare.

Okay, I will have a look at the Skellington then.

Anyhow, about another half an hour before the university closes today (Sunday internet time is short (T_T)), so I better sort things out :) :).

Thanks for the advice

Deathworks
Flat vs. nested is more a reference to code blocks. That is, if your code blocks are too deeply nested (and you'll notice by the way your indentation gets bigger and bigger) then perhaps your code structure needs some thought (eg. splitting off some code into new functions). The Zen of Python also thinks namespaces are a honking great idea :)

Modules don't change namespaces - they just are one. And you access their namespace from another module by importing them and using the dotted notation, "import time", "time.time()" or "import globals" and "globals.Basic_Map".