Attachment 'Image2PyFile.PY'

Download


"""
Image2PyFile.py
    
    Convert disk graphics image files to importable Pyhon code.
    
    This code is intended for embedding it in any Python application
    so the graphics image data is available without having to read
    external disk graphics image files at runtime.  
    
    Inspired from img2py.py by Robin Dunn.
      img2py.py crashes on Win7 64-bit as of 2012-04-26.
    
Command Line Usage:
    > python.exe Image2PyFile.py [options] imgFile1 [ imgFile2 ...]  \ 
                    [ pyImagesExist1.py ...] pyImagesNew.py
    
Usage Within a Python Application:
    import Image2PyFile as i2p
    i2p.Image2PyFile( [options] imgFile1 [ imgFile2 ...]  \ 
                    [ pyImagesExist1.py ...] pyImagesNew.py )
    
    Arguments for image files, options and .py pyImages files 
    may intermixed in any order.
    
    NOTE:
        All filenames ending with ".py" are assumed to be PyImages files.
        The last given .py file will always be written or rewritten.
        Any .py filenames preceding the last are assumed to exist.
        
        The directory of the output pyImage filename determines
        where that file will be written.
    
    ACTION, PROGRESS, NOTE and WARNING message are written to stdout.
    
    FAILURE messages indicate file I/O failures.
    They do NOT terminate the application and these messages 
       are wriiten to stderr.

    ERROR messages indicate a fatal application error and are wriiten
       to stderr. They cannot be suppressed, thus there are 
       no associated command line options available for them .
    
Options: (NOTE: The following options are just that:  Optional !
                None *need* to be specified.)
    
    -a -A   Enable/Disable ACTION   messages. Disabled by default.
    -f -F   Enable/Disable FAULURE  messages. Enabled by default.
    -n -N   Enable/Disable NOTE:    messages. Disabled by default.
    -p -P   Enable/Disable PROGRESS messages. Enabled by default.
    -w -W   Enable/Disable WARNING  messages. Disabled by default.
    
    -q -Q   Disable/Enable all non-fatal messages.
            To enable only specific messages specify '-q' followed
            with the desired lower case message option letters.
            E.g.:  -qfw
            E.g.:   -q -f -w
            
    -h  List this help and exit.     
    -H  List this help THEN CONTINUE.
    
    A PROGRESS message indicates an attempt to do something
      such as read a file while an ACTION message indicates
      an actual accomplishment.

This module can be used internally by instantiating 
  an Image2PyFile() class object. 
  
E.g., the following:
    
    - Disables all messaging except for PROGRESS messages;
    - Reads all existing image data from file pyimagesIn.py and
    -   appends them to file pyimagesOut.py;
    - Processes images from all give .PNG files and writes their
        data to pyimagesOut.py
    
    import Image2PyFile as i2p
    args = '-qp image1.png pyimagesIn.py pyImagesOut.py image2.gif'
    args = args.split()     # Args are in a list just like sys.argv[ 1: ]
    i2p.Image2PyFile( args )

E.g.:  
An initial command line runs and its results when file PyImages.PY 
  does not yet exist:

    > python.exe Image2PyFile.py  About-16.PNG About-32.PNG PyImages.PY
    PROGRESS:  Reading New Image Files
    ACTION:  Adding Image: [ About_16_PNG ]  File: [ About-16.PNG]
    ACTION:  Adding Image: [ About_32_PNG ]  File: [ About-32.PNG]
    PROGRESS:  Writing New PyImages File: [ PyImages.PY ]

A following run when file PyImages.PY does exist:
    
    > python.exe Image2PyFile.py  Cut-32.PNG Delete-16.PNG
    PROGRESS:  Reading EXISTING PyImages File: [ PyImages.PY ]
    PROGRESS:  Reading New Image Files
    ACTION:  Adding Image: [ Cut_32_PNG ]  File: [ Cut-32.PNG]
    ACTION:  Adding Image: [ Delete_16_PNG ]  File: [ Delete-16.PNG]
    PROGRESS:  OVERWRITING PyImages File: [ PyImages.PY ]


Ray Pasco
pascor(at)verizon(dot)net

Version:
    1.2     2102-04-29  All functions specific to pyImages files are now methods
                        within the Image2PyFile class.  PyImages files not located
                        in os.cwd are now read properly (they're imported).
                        Better file existance checks.
                        The last PyImages file (the output) is no longer read in
                        to accumulate its image data.
    1.1     2012-04-26  Better messaging. Bug fixes.

Tested on:

    Windows   6.1.7601  {Windows 7 64-bit}
    Python    2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)]
    Wx Version 2.8.12.0
    Wx Pltform ('__WXMSW__', 'wxMSW', 'unicode', 'wx-assertions-on', 'SWIG-1.3.29')

    Windows   5.1.2600  {Windows XP 32-bit}
    Python    2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)]
    Wx Version 2.8.12.0
    Wx Pltform ('__WXMSW__', 'wxMSW', 'unicode', 'wx-assertions-on', 'SWIG-1.3.29')
"""

import os, sys
import getopt
import glob
import re
import tempfile
import base64
import binascii
import wx

import time

try :
    THISPYFILENAME = os.path.basename( __file__ )
except NameError :    # Doesn't exist if this app is frozen.
    THISPYFILENAME = 'Image2PyFile.exe'

#------------------------------------------------------------------------------
#==============================================================================
#------------------------------------------------------------------------------

class Image2PyFile() :
    """Convert disk graphics image files to embeddable Python code.
    
Image2PyFile allows embedding external disk graphics image files's data
in any Python application so that data is available without having to 
read external files at runtime.  
    """
    
    def __init__( self, args=None ) :
        defName = 'Image2PyFile()'
        
        enabledMsgs, fileArgs, opts = self.ProcessArgs( args )
        
        inPyImageFilesList, outPyImageFileName, imageFileList =  \
                    self.SortFilenames( fileArgs, enabledMsgs )
        
        # Determine the dir for the output pyImages file.
        outPyImageFileDir, outPyImageFileName =  \
                        os.path.split( os.path.abspath( outPyImageFileName ) )
        
        if not wx.GetApp() :                # Has the user's program already created a wx.App ?
            #app = wx.App( redirect=True )   # Create a dummy app to enable accessinng all wx's methods.
            app = wx.App( redirect=False )   # Create a dummy app to enable accessinng all wx's methods.
        
        #-----
        
        # Import any given PyImages files and populate an imageDict from their contents.
        # There must be at least one pyImages file. 
        # All input PyImages files have already been checked for existance.
        imageDict = dict()      # Brand new, empty
        for anInpPyImgFileGiven in inPyImageFilesList :
            
            anInpPyImgFilePath = os.path.abspath( anInpPyImgFileGiven )
            anInpPyImgFileDir, anInpPyImgFileName =  os.path.split( anInpPyImgFilePath )
            
            if ('progress' in enabledMsgs) :
                print 'PROGRESS:  Reading EXISTING PyImages File [ %s ]  in:\n  [ %s ]'  \
                      %( anInpPyImgFileName, anInpPyImgFileDir )
            
            # Add to imageDict{} given the current content of imageDict{}.
            imageDict = self.ImportPyImages( anInpPyImgFilePath, imageDict, enabledMsgs )
            
            #ListImageDict( imageDict )        # DEBUG
        
        #end for
        
        #-----
        
        if imageFileList :      # Might be "None" if just accumulating pyImagefile image defs.
            
            if ('progress' in enabledMsgs) :
                print 'PROGRESS:  Reading New Image Files'
            
            # Add the new images to imageDict
            for imgFilename in imageFileList :
                
                # Convert the filename to a legal Python identifier string.
                baseName  = os.path.basename( imgFilename )
                mainName, fileExt = os.path.splitext( baseName )
                idName = String2Identifier( baseName )
                
                # Check if the image name already exists imageDict.
                overWriting = False
                for imgObj in imageDict.values() :
                    
                    if (idName == imgObj.idName) :
                        overWriting = True
                        break           # Process the next image filename.
                        #-----
                
                #end for
                
                # Convert any type image to an equivalent PNG file data format.
                # Result will be set to None if any kind of error occurs.
                pngImageData = CreatePngFileData( imgFilename, enabledMsgs )
                if pngImageData :
                    
                    # This is all images' storage format.
                    encPngData = B64EncodeBinaryData( pngImageData )

                    # Create a new ImageObject and populate it.
                    imgObj = ImageObject( idName, baseName, mainName, fileExt, encPngData )
                    
                    # Add (accumulate) the new encoded image data object to the image dictionary.
                    imageDict[ idName ] = imgObj
                    
                    if overWriting :
                        if ('warning' in enabledMsgs) :
                            print 'WARNING:  OVERWITING PyImage: [ %s ]  from Existing File: [ %s ]'  \
                                  %( idName, baseName )
                    else :
                        if ('action' in enabledMsgs) :
                            print 'ACTION:  Adding PyImage: [ %s ]  from Existing File: [ %s ]'  \
                                  %( idName, baseName )
                   #end
                   
                #end if pngImageData
                
            #end for imgFilename in imageFileList
        #end if imageFileList
        
        #ListImageDict( imageDict )     # DEBUG
        
        #-----
        
        # Write a brand new new PiImages.py file, overwriting any existing one
        #   of the same name.
        outPyImageFilePath = os.path.join( outPyImageFileDir, outPyImageFileName )
        
        self.WritePyImageFile( imageDict, outPyImageFilePath, enabledMsgs )
        
    #end __init__
    
    #----------------------------------

    def WritePyImageFile( self, imageDict, outPyImageFilePath, enabledMsgs ) :
        """ Writes a new pyImages file from an imageDict. """
        defName = DefName()     # For DEBUG

        outPyImageFileDir, outPyImageFileName = os.path.split( outPyImageFilePath )
        
        if not imageDict :      # Is an empty dict
            if ('warning' in enabledMsgs) :
                print 'WARNING:  NO IMAGES TO WRITE to PyImages File: [ %s ]'  \
                                  %( outPyImageFileName )
            return
            #-----

        if (not os.path.exists( outPyImageFilePath )) :
            if ('progress' in enabledMsgs) :
                print 'PROGRESS:  Writing NEW PyImages File: [ %s ] in:\n  [ %s ]'  \
                                  %( outPyImageFileName, outPyImageFileDir )

        else :
            if ('progress' in enabledMsgs) :
                print 'PROGRESS:  OVERWRITING PyImages File: [ %s ] in:\n  [ %s ]'  \
                                  %( outPyImageFileName, outPyImageFileDir )

        # If one already exist, delete it and rewrite it from scratch
        #   appending any new image defs that have been accumulated.
        pyImagesFile = open( outPyImageFilePath, 'w' )    # Delete any existing file.

        # Write the header once,
        self.WritePyImagesFileHeader( pyImagesFile )

        # For each imgObject write its encoded data and functions.
        for imgObj in imageDict.itervalues() :
            self.WritePyImageDataAndFuncs( imgObj, pyImagesFile, enabledMsgs )

        pyImagesFile.close()

    #end WritePyImageFile def

    #----------------------------------

    def ProcessArgs( self, args=None ) :
        """
        Command line parsing. Separates args into three groups:
        1) List of pyImages files that will be read from and/or written to;
        2) A Python set of message type strings indicating which kinds 
              of message types will be enabled to be emitted if necessary.
        """
        defName = DefName()     # For DEBUG

        if not args :
            msg = 'ERROR:  %s::%s:  No Commandline Args Given\n'  \
                  %( THISPYFILENAME, defName )
            print >> sys.stderr, msg
            print >> sys.stderr, __doc__
            sys.exit(1)
        #end if

        #-----

        try :
            opts, fileArgs = getopt.gnu_getopt( args, 'aA fF hH nN pP qQ wW' )

        except getopt.GetoptError :

            msg = '\nERROR:   %s::%s:  UNKNOWN SWITCH ARG in:\n\n%s\n'  \
                  %( THISPYFILENAME, defName, args )
            print >> sys.stderr, msg
            sys.exit(1)

        #-----

        # [ opts ] is returned as list of tuples.
        # A set is used rather than a tuple because sets automatically
        #   prevent duplicate items when added multiple times.
        enabledMsgs = set( [ 'action', 'progress' ] )   
        for key, valIgnored in opts :

            if   key == '-a' :
                enabledMsgs.add( 'action' )        # Enable these messages
            elif   key == '-A' :
                enabledMsgs.discard( 'action' )    # Suppress these messages

            elif key == '-f' :
                enabledMsgs.add( 'failure' )
            elif key == '-F' :
                enabledMsgs.discard( 'failure' )

            elif key == '-h' :
                print __doc__
                sys.exit(1)
            elif key == '-H' :
                print __doc__

            elif key == '-n' :
                enabledMsgs.add( 'note' )
            elif key == '-N' :
                enabledMsgs.discard( 'note' )

            elif key == '-p' :
                enabledMsgs.add( 'progress' )
            elif key == '-P' :
                enabledMsgs.discard( 'progress' )

            elif key == '-q' :
                enabledMsgs = list()        # Suppress all messages
            elif key == '-Q' :
                enabledMsgs.add( 'action' )        # Enable these messages
                enabledMsgs.add( 'failure' )
                enabledMsgs.add( 'note' )
                enabledMsgs.add( 'progress' )
                enabledMsgs.add( 'warning' )

            elif key == '-w' :
                enabledMsgs.add( 'warning' )
            elif key == '-W' :
                enabledMsgs.discard( 'warning' )

            else :
                print >> sys.stderr, 'UNRECOGNIZED Arg Switch  [ %s ]' %( key )
            #end if

        #end for
        ##print 'enabledMsgs:  ', enabledMsgs; print    # DEBUG

        if (len( fileArgs ) < 1) :      # Sanity check.

            msg = '\nERROR - %s::%s:   At Least One PyImages Filename Must Be Given.\n'  \
                  %( THISPYFILENAME, defName )
            print >> sys.stderr, msg
            sys.exit(1)

        #-----

        return (enabledMsgs, fileArgs, opts)

    #end ProcessArgs

    #----------------------------------

    def ListImageDict( self, imageDict ) :
        """ Simple DEBUG utiliy to list all an imageDict's values 
            except for the image data.
        """
        defName = DefName()     # For DEBUG

        print '\n----  imageDict :'
        for imgObj in imageDict.itervalues() :
            print '  baseName   = %s' %( imgObj.baseName )
            print '  createdExt = %r' %( imgObj.createdExt )
            print '  mainName   = %s' %( imgObj.mainName )
            print '  idName     = %s' %( imgObj.idName )
            print
        #end for

    #end def

    #------------------------------------------------------------------------------

    def SortFilenames( self, fileArgs, enabledMsgs ) :
        """ Sorts a list of filenames into sets:
        
        1) inPyImageFilesList - a list of pyImage files to be input;
        2) outPyImageFileName - the pyImages filename intended to be written;
        3) imageFileList - a list of image files to add the the output pyImages file.

        At minimum, one pyImages filename must be given in the args.
        The last given pyImages file will be the output pyImages file.
        This determines where the output pyImages file will be written.

        It doen't have to exist if 1 or more previous existing piImages.py files 
        have been given.

        A lone given pyImages filename will be both input and then overwritten.
        """
        defName = DefName()     # For DEBUG

        allPyImageFilesList = list()
        imageFileList       = list()

        # Sort the filenames in two groups: 1) PyImages files,  2) image files.
        for filename in fileArgs :

            if filename.lower().endswith( '.py' ) :     # A pyImages filename.

                allPyImageFilesList.append( filename )

            else :      # Must be an image filename.

                if os.path.exists( filename ) :
                    imageFileList.append( filename )
                else :
                    if ('warning' in enabledMsgs) :
                        print 'WARNING:  Input Image File DOES NOT EXIST  [ %s ]'  \
                              %( filename )
                #end if os.path.exists

            #end if filename.lower().endswith( '.py' )

        #end for filename in fileArgs

        # Create inpPyimgFileList and outPyImageFileName from allPyImageFilesList.
        numTotalPyImgFiles = len( allPyImageFilesList )
        inPyImageFilesList = list()
        if (numTotalPyImgFiles == 0) :

            print >> sys.stderr, 'ERROR:  %s::%s:   NO GIVEN Output PyImages Filename\n'  \
                                 %( THISPYFILENAME, defName )
            sys.exit(1)
            #-----
        
        else :
            # It's not necessary that this file already exist.
            outPyImageFileName = allPyImageFilesList[ -1 ]    # ALWAYS the last filename in the list.

            if numTotalPyImgFiles == 1 :

                pass     # DON'T include the output .py file.

            else :  # numTotalPyImgFiles >= 2

                allPyImageFilesList = allPyImageFilesList[ :-1 ]    # Check all but the last the given files.
                for anInpPyImgFile in allPyImageFilesList :         # The last is ALWAYS the output file.

                    if (not os.path.exists( anInpPyImgFile )) :

                        if ('failure' in enabledMsgs) :
                            msg = 'FAILURE:  Given Input PyImages File DOES NOT EXIST:\n  [ %s ]'  \
                                  %( anInpPyImgFile )
                            print msg
                    else :
                        inPyImageFilesList.append( os.path.abspath( anInpPyImgFile ) )

                #end for
            #end if
        #end if
        
        if (not imageFileList) :
            if ('note' in enabledMsgs) :
                print 'NOTE:  NO GIVEN Image Files to Encode\n'

        """ DEBUG
        print '$$$$  inPyImageFilesList'
        print '     ', inPyImageFilesList
        print
        print '$$$$  outPyImageFileName'
        print '     ', outPyImageFileName
        print
        print '$$$$  imageFileList'
        print '     ', imageFileList
        print
        """

        return inPyImageFilesList, outPyImageFileName, imageFileList

    #end SortFilenames def

    #----------------------------------

    def ImportPyImages( self, inpPyImgFileNameGiven, imageDict, enabledMsgs ) :
        """ Import the pyImages file and add its images to imageDict{}.
        The file's existance is expected to have been already verified.
        """
        defName = DefName()     # For DEBUG
        
        inpPyImgFilePath = os.path.abspath( inpPyImgFileNameGiven )
        inPyImagesFileDir, inPyImagesFileName = os.path.split( inpPyImgFilePath )
        
        # Tempoirarily add to sys.path in case the pyImages file is located somewhere 
        #   other then os.cwd().
        sys.path.insert( 0, os.path.join( inPyImagesFileDir ) )
        
        # Remove the file extension to be able to import the file.
        pyImagesFileMainName, pyImagesFileExt = os.path.splitext( os.path.basename( inpPyImgFileNameGiven ) )
        pyImportStr = 'from %s import *' %( pyImagesFileMainName )
        
        if (os.path.exists( os.path.abspath( inpPyImgFileNameGiven ) )) :
            
            try :
                exec( pyImportStr )
            except :                # Is a FAILURE, but not a fatal error.
                if ('failure' in enabledMsgs) :
                    if os.path.exists( inpPyImgFileNameGiven ) :
                        print >> sys.stderr, 'FAILURE:  PyImages File Has BAD PYTHON SYNTAX  [ %s ]  in:'  \
                                          %( inPyImagesFileName )
                        print >> sys.stderr, '  [ %s ]' %( inPyImagesFileDir )
                #end if
                del sys.path[ 0 ]   # Remove the temporary search path.

                return imageDict    # Return an empty dictionary.
                #-----

            #end try
            
        else :
            print >> sys.stderr, 'FAILURE:  PyImages File DOES NOT EXIST  [ %s ]'  \
                              %( inpPyImgFileNameGiven )
        #end if
        del sys.path[ 0 ]   # Remove the temporary search path.
        
        # Check if the Py file is actually a PyImages file.
        try:
            catalog.iteritems()     # Should have been defined
        
        except NameError :
            if ('failure' in enabledMsgs) :
                msg = 'FAILURE:  NOT A PYIMAGES File:  [ %s ]' %( inpPyImgFileNameGiven )
                print >> sys.stderr, msg
            #end if

            return imageDict
            #-----
        
        #end try
        
        # Assume, for now, that the key is a baseName and not a mainName.
        for baseName, catIdName in catalog.iteritems() :
            
            # Construct the callable function name from the catalog's idName.
            funcStr = 'GetData_' + catIdName + '()'
            pngImageData = eval( funcStr )
            
            # Encode the binary data into viewable 7-bit characters.
            encPngData = B64EncodeBinaryData( pngImageData )
            
            # Create an ImageObject for each encoded image and add it to imageDict.
            # Assume that the baseName from the catalog dict has an extension appended.
            # ImageObject () will create a nominal extName if none exists in baseName.
            imgObj = ImageObject( idName=catIdName, baseName=baseName, encPngData=encPngData )
            
            newIdName = imgObj.idName       # Extract the possibley-corrected idName.
            
            # Check if idName already exists.
            alreadyInDict = False
            for anIdName in imageDict.iterkeys() :
                
                # imageDict keys are the corrected idNames.
                if (newIdName == anIdName) :
                    alreadyInDict = True
                    break       # Search no further - dictionaries can't have duplicate keys.
                    #-----
                    
            #end for
            
            baseName = imgObj.baseName
            if alreadyInDict :
                if ('warning' in enabledMsgs) :
                    print 'WARNING:  REDEFINING PyImage: [ %s ]  Original Filename: [ %s ]'  \
                          %( newIdName, baseName )
            else :
                if ('note' in enabledMsgs) :
                    print 'NOTE:  Reading PyImage: [ %s ]  Original Filename: [ %s ]'  \
                          %( newIdName, baseName )
            #end if
            
            # Use the extension-appended idName for the key, 
            #   not the original, possibly extension-less idName read from the pyImages file.
            imageDict[ imgObj.idName ] = imgObj     
            
        #end for baseName, catIdName in catalog.iteritems()
        
        return imageDict
        
    #end ImportPyImages def
    
    #----------------------------------
    
    def WritePyImagesFileHeader( self, pyImagesFile ) :
        """ Writes syntactically correct Python statements to the output pyImages file.
        """
        defName = DefName()     # For DEBUG
        
        pyImagesFile.write( '#' + '-'*69 + '\n\n' )
        line = '# This file was generated by %s\n\n' %( THISPYFILENAME )
        pyImagesFile.write( line )
        pyImagesFile.write( 'from wx.lib.embeddedimage import PyEmbeddedImage\n' )
        pyImagesFile.write( '\n' )
        pyImagesFile.write( 'catalog = {}\n' )
        
    #end WritePyImagesFileHeader def
    
    #----------------------------------
    
    def WritePyImageDataAndFuncs( self, imgObj, pyImagesFile, enabledMsgs ) :
        """ Writes the Python code to the output pyImages file that both defines image data 
        and function defs that can read generate the associated  image data, wx.Image, 
        wx.Bitmap and wx.Icon objects when its pyImmages file is imported by a Python app
        and the appropriate function calls are made.
        """
        defName = DefName()     # For DEBUG
        
        pyImagesFile.write( '#' + '-'*69 + '\n\n' )
        pyImagesFile.write( '%s = PyEmbeddedImage( \n%s\n'  \
                            %( imgObj.idName, imgObj.encPngData) )
        pyImagesFile.write( '    )\n\n' )   # Data definition statement termination

        # When the PyImages file is imported the following dictionary idName value 
        #   will become part of the function names.
        pyImagesFile.write( 'catalog["%s"] = "%s"\n' %( imgObj.baseName, imgObj.idName) )

        pyImagesFile.write( 'GetData_%s   = %s.GetData\n'   %( imgObj.idName, imgObj.idName ) )
        pyImagesFile.write( 'GetImage_%s  = %s.GetImage\n'  %( imgObj.idName, imgObj.idName ) )
        pyImagesFile.write( 'GetBitmap_%s = %s.GetBitmap\n' %( imgObj.idName, imgObj.idName ) )
        pyImagesFile.write( 'GetIcon_%s   = %s.GetIcon\n'   %( imgObj.idName, imgObj.idName ) )
        pyImagesFile.write( '\n' )

        """  DEBUG
        print '$$$$  Image2PyFile::ImageFileEncodeAndWrite:'
        print ' '*8 + 'Embedding image as callable identifier [ %s ]' %( imgObj.idName )
        """

    #end WritePyImagesFileFunctions def
    
#end Image2PyFile class

__all__ = [ Image2PyFile ]

#------------------------------------------------------------------------------
#==============================================================================
#------------------------------------------------------------------------------

class ImageObject() :
    """Encoded image data and meta information storage.  
    
A collection of useful attributes created when reading image data from 
existing pyImages files. This also contains all info needed for generating 
a new embeddable PyImage filew encoded image entry.
    """
    
    def __init__( self, idName='', baseName='', mainName='', fileExt='', encPngData='' ) :
        defName = 'ImageObject()'
        
        # Instantiate, while documenting, all the necessary attributes.
        self.idName     = idName        # Suitable for use as a Python identifier.
        self.baseName   = baseName      # The image's source file basename.
        self.mainName   = mainName      # The "main" part of the source basename
        self.fileExt    = fileExt       # The extension of the image source file basename
        self.createdExt = False         # First assume a baseName was originally supplied.
        self.encPngData = encPngData    # Image's .PNG file binary data
        
        # All the most common 3-letter image file extensions that wxPython can read.
        # To Do: jpeg, tiff, any other non-3-letter image file extensions (if there are any).
        IMG_EXTS = [ '.bmp', '.gif', '.ico', '.jpg', '.png', '.tif' ]
        
        if baseName :       # [is given];  A file extension may or may not be appended.
            
            extLen = 4      # Includes the leading dot char.
            extStartIndex = len( self.baseName ) - extLen   # Doesn't now allow for 4-letter ext's.
            
            # Is the file extension appended to the mainName ?
            if self.baseName.lower()[ extStartIndex: ] in IMG_EXTS :   
                
                self.mainName, self.fileExt = os.path.splitext( baseName )
            
            else :      # File extension is not appended:  Asume a .PNG file.
                
                self.mainName = self.baseName
                self.fileExt = '.png'
                self.createdExt = True
                self.baseName = self.baseName + self.fileExt
                
        elif self.mainName :    # [is given] Has no fileExt
            
            if self.fileExt :   # The fileExt is defined, so create the baseName.
                if not self.fileExt.startswith( '.' ) :
                    self.fileExt = self.fileExt + '.'
                
                self.baseName = self.mainName + self.fileExt
                
            else :              # No fileExt defined - create a reasonable one.
                
                print >> sys.stderr, '\nERROR:  %s::%s:  NEITHER baseName NOR mainName Given.'  \
                                  %( THISPYFILENAME, defName )
                sys.exit(1)
                #-----
                
        #end if self.baseName is defined
        
        self.idName = String2Identifier( self.baseName )
        
    #end __init__
    
#end ImageObject class

#------------------------------------------------------------------------------

def WhatsInstalled() :
    """
    List the versions of Python and wxPython.
    """
    defName = DefName()     # For DEBUG
    
    # Which vesrions of Python and wxPython are installed ?
    import os, sys, platform
    print
    print defName + '():'
    if os.name == 'nt' :
        print 'Windows  ', platform.win32_ver()[1]
    else :
        print 'Platform ', platform.system()
    #end if
    print 'Python   ', sys.version
    addon_pkgs = [ ('Wx Version', 'wx.VERSION_STRING'),
                   ('Wx Pltform', 'wx.PlatformInfo'),   ]
                   
    for addonStr, attribute in addon_pkgs :
        try :
            print addonStr, eval( attribute )
        except NameError :
            print
        #end try
    #end for
    print
    
#end WhatsInstalled def

#------------------------------------------------------------------------------

def CallersName():
    return sys._getframe(2).f_code.co_name

#------------------------------------------------------------------------------

# Credit to Alex Martelli, "Python Cookbook" p. 440
def DefName() :
    """
    Returns the name of the function calling DefName.
    Does not work inside class __init__() definitions.
    """
    import sys
    return sys._getframe(1).f_code.co_name + '()'

#end def

#------------------------------------------------------------------------------

def String2Identifier( s ) :
    """
    Simple character substitution to produce a legal Python identifier string.
    """
    defName = DefName()     # For DEBUG
    
    letters = []
    for letter in s :
        if not letter.isalnum() :
            letter = '_'
        letters.append( letter )
    #end for
    
    if not letters[ 0 ].isalpha() and letters[ 0 ] != '_' :
        letters.insert( 0, '_' )
    identifierString = ''.join( letters )
    
    return identifierString
    
#end String2Identifier def
    
#------------------------------------------------------------------------------

def B64Encode( s, altchars=None ) :
    """
    Encode a string using Base64. Meant for processing binary strings 
    into readable characters.

    Optional altchars must be a string of at least length 2 (additional 
    characters are ignored) which specifies an alternative alphabet for 
    the '+' and '/' characters.  This allows an application, e.g., 
    to generate a url or filesystem-safe Base64 strings.

    The encoded string is returned.
    """
    defName = DefName()     # For DEBUG
    
    # Eliminate the trailing newline returned.
    encoded = binascii.b2a_base64( s )[ :-1 ]
    
    if altchars :
        encoded = _translate( encoded, {'+': altchars[0], '/': altchars[1]} )
        
    return encoded

#end B64Encode def

#------------------------------------------------------------------------------

def BitmapToPngFile( wxBmap, outFileDir=None, outFileName='', enabledMsgs=None ) :
    """
    Save a wx.Bitmap to a PNG file. The binary contents of this file is intended
    to be b64 encoded in order to save the encoded data to the output pyImages file.
    
    Returns None if any error occurs.
    """
    defName = DefName()     # For DEBUG
    
    if outFileName :
        outFilePathname = outFileName

    else :
        
        newMainName = os.path.basename( os.path.splitext( imgFilename )[0] )
        newfileBasename = newMainName + '.png'
        outFilePathname = os.path.join( outFileDir, newfileBasename )
    #end if

    if wxBmap.SaveFile( outFilePathname, wx.BITMAP_TYPE_PNG ) :     # SaveFile() success

        return True

    else :      # wx.Bitmap.SaveFile() has failed.
        if ('failure' in enabledMsgs) :
            msg = '\nFAILURE:  CAN\'T WRITE Temporary wx.Bitmap to .PNG file.\n'
            print >> sys.stderr, msg
        #end if
        
        # Try a different save tactic.
        wxImage = wx.ImageFromBitmap( wxBmap )     # ? Avoid some early wx version limitation ?
        if wxImage.SaveFile( outFilePathname, wx.BITMAP_TYPE_PNG ) :
            return True
            
        else :
            msg  = '\nERROR:   CAN\'T WRITE Temporary wx.Image .PNG  File: [ %s ]\n\n'  \
                   %( outFilePathname )
            print >> sys.stderr, msg
            sys.exit(1)
        #end if
        
    #end if wxBmap.SaveFile()
    
#end BitmapToPngFile def

#------------------------------------------------------------------------------

def CreatePngFileData( imgFilename, enabledMsgs ) :
    """
    Encode a PNG file's lossless-compressed binary image data 
      into a single, big b64 string.
    """
    defName = DefName()     # For DEBUG
    
    if not os.path.exists( imgFilename ) :
        if ('warning' in enabledMsgs) :
            msg = '\nWARNING:  Image File Doesn\'t Exist[ %s ].' %( imgFilename )
            print msg
        
        return None
        #-----
    
    # Predefine an error message in case it's needed.
    # It may get used for one of 2 different errors.
    msg = '\nFAILURE:  Image File is UNREADABLE  [ %s ]' %( imgFilename )
    try :
        wxBmap = wx.Bitmap( imgFilename, wx.BITMAP_TYPE_ANY )
    
    except :
        if ('failure' in enabledMsgs) :
            print >> sys.stderr, msg
        return None
        #-----
    
    # Handle bad image file data
    if (not wxBmap.Ok())  and  ('failure' in enabledMsgs) :
        print >> sys.stderr, msg        # Same msg string from above.
        ##print '5'*70                  # DEBUG
        return None
        #-----
    
    # Read the original image file and write it to a new PNG file.
    tmpPngFilename = tempfile.mktemp()
    
    success = BitmapToPngFile( wxBmap, None, tmpPngFilename, enabledMsgs )
    if not success :
        print >> sys.stderr, '\nERROR:  CAN\'T WRITE to Temporary PNG File.\n'  \
                             %( THISPYFILENAME, defName )
        return None
        #-----
    
    # Encode the PNG file's lossless-compressed binary image data into a single, big b64 string.
    pngFile = open( tmpPngFilename, 'rb' )
    pngImageData = pngFile.read()
    pngFile.close()
    os.remove( tmpPngFilename )
    
    return pngImageData
    
#end CreatePngFileData def

#------------------------------------------------------------------------------

def B64EncodeBinaryData( pngImageData ) :
    """
    B64 encodes a binary byte string. Returns a list of lines of strings
      suitable for directly embedding into a PyImages file.
    """
    defName = DefName()     # For DEBUG
    
    # Encode the PNG file's lossless-compressed binary image data into a single, big b64 string.
    encPngImgData = B64Encode( pngImageData )
    
    # Chop the long b64 character-encoded encPngImgData into manageable 
    #   line lengths for writing to a file.
    linesOfEncPngImgData = list()   # b64 linesOfEncPngImgData list.
    while encPngImgData :
        
        aLineOfEncPngImgData = encPngImgData[ :72 ]     # A chunk length of 72 chars
        encPngImgData = encPngImgData[ 72: ]            # The remainder of data to be encoded.
        aLineOfEncPngImgData = '    "%s"' %( aLineOfEncPngImgData )
        
        linesOfEncPngImgData.append( aLineOfEncPngImgData )  # Add to linesOfEncPngImgData list.
        
    #end while encPngImgData
    linesOfEncPngImgData = '\n'.join( linesOfEncPngImgData )
    
    return linesOfEncPngImgData
    
#end B64EncodeBinaryData def

#==============================================================================

if __name__ == '__main__' :
    
    #WhatsInstalled()       # List this platform's Puthon and wxPython version info.
    
    """
    try :
        #print "\n$$$$$$$$  getattr( sys, 'frozen', '' ) = [ %r ]" %( getattr( sys, 'frozen' ) )
        print
    except AttributeError :
        print "\nAttributeError:  'module' object has no attribute 'frozen'\n"
    """
    
    cmdLineArgs = sys.argv[ 1: ]
    i2p = Image2PyFile( cmdLineArgs )
    
    #print '\ni2p.__doc__ :\n', i2p.__doc__
    
#end if

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2012-05-04 19:57:15, 33.3 KB) [[attachment:EditorPanel_Demo.png]]
  • [get | view] (2012-05-04 19:57:22, 26.8 KB) [[attachment:EditorPanel_Demo.py]]
  • [get | view] (2011-05-26 18:16:27, 2.6 KB) [[attachment:EmbeddedGraphicData.py]]
  • [get | view] (2011-05-26 06:11:57, 2.9 KB) [[attachment:Hand.png]]
  • [get | view] (2012-04-30 18:47:53, 38.0 KB) [[attachment:Image2PyFile.PY]]
  • [get | view] (2012-04-26 21:18:00, 31.9 KB) [[attachment:Image2PyFile.py]]
  • [get | view] (2012-04-30 18:36:42, 7.1 KB) [[attachment:Image2PyFile_GUI.py]]
  • [get | view] (2012-04-27 03:22:42, 20.5 KB) [[attachment:Image2PyFile_GUI_Output.png]]
  • [get | view] (2011-05-26 06:41:02, 42.1 KB) [[attachment:Image2PyFile_GUI_Output_Again.png]]
  • [get | view] (2012-04-26 23:27:42, 26.8 KB) [[attachment:Image2PyFile_Output.png]]
  • [get | view] (2011-05-26 06:12:55, 2.9 KB) [[attachment:Minus.png]]
  • [get | view] (2011-05-26 06:13:18, 2.9 KB) [[attachment:Plus.png]]
  • [get | view] (2011-05-26 18:13:55, 2.8 KB) [[attachment:Separator.png]]
  • [get | view] (2012-05-04 19:44:48, 19.8 KB) [[attachment:wxToolBar_Embedded_Demo.png]]
  • [get | view] (2012-05-04 19:45:04, 11.2 KB) [[attachment:wxToolBar_Embedded_Demo.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.