# Python 2.3.5, PIL 1.1.5b1 for Py 2.3, wxPython 2.5.4.1, MSW XP

import wx
import os
import Image
import imageconverts
#import colorspaces


print 'wx.VERSION_STRING = ', wx.VERSION_STRING

def ImScaledFromFile (filepathname, extentsize):

    # If too large, resize the image to fit in the panel space.
    imPil = Image.open (filepathname)
    imXsize, imYsize = imPil.size

    xExtent = extentsize [0]    # border size estimations for MS Windows
    yExtent = extentsize [1]
    
    if (imXsize > xExtent) or (imYsize > yExtent):

        # Find which axis is the primary, i.e., the axis that limits the maximum scaled size.
        x2yExtentRatio = 1.0 * xExtent / yExtent
        imageXYratio = 1.0 * imXsize / imYsize

        if imageXYratio > x2yExtentRatio:       # The X axis is the primary axis

            # Find the image scale factor using the X axis.
            scalefactor = (1.0 * xExtent) / imXsize
        else:
            scalefactor = (1.0 * yExtent) / imYsize
        #end if

        # Resize the original image
        newXsize = int (scalefactor * imXsize)
        newYsize = int (scalefactor * imYsize)
        imPil = imPil.resize ( (newXsize, newYsize) )
    #end if

    thumbnailsize = (newXsize, newYsize)

    return (imPil, thumbnailsize)

#end def ImScaledFromFile

class ProportionalSplitter (wx.SplitterWindow):
    ''' Horizontal and vertical splitter windows management '''

    def __init__ (self, parent, id=-1, proportion=0.50, minsize=50):

        self.sashlocked = False     # set this attribute to prevent user sash position adjustment.

        self.proportion = proportion
        if not (0.0 < self.proportion < 1.0):
            raise ValueError, "ProportionalSplitter proportion value must be between 0.0 and 1.0"
	    #end if

        wx.SplitterWindow.__init__ (self, parent, id, style=wx.SP_3D)
        self.SetMinimumPaneSize (minsize)    # min pane size so no panes will dissapear on extreme resize

        self.ResetSash()
        wx.EVT_SIZE (self, self.OnReSize)
        wx.EVT_SPLITTER_SASH_POS_CHANGED  (self, self.GetId(), self.OnSashChanged)  # for unlocked sashes
        wx.EVT_SPLITTER_SASH_POS_CHANGING (self, self.GetId(), self.OnSashChanging) # for locking sashes

        # hack to set sizes on first paint event
        wx.EVT_PAINT (self, self.OnPaint)
        self.firstpaint = True

    #end def __init__

    def SplitHorizontally (self, win1, win2):

        if self.GetParent() is None:    return False

        framehgt = self.GetParent().GetSize().GetHeight()
        framehgt = max ((framehgt - 60), 0)     # avoid apparent bug
        paneheight = int (round (framehgt * self.proportion))

        success = wx.SplitterWindow.SplitHorizontally (self, win1, win2, paneheight)
        return success
    #end def splitHorizontally

    def SplitVertically (self, win1, win2):
        if self.GetParent() is None:    return False

        framewth = self.GetParent().GetSize().GetWidth()
        framewth = max ((framewth - 8), 0)     # avoid apparent bug
        panewidth = int (round (framewth * self.proportion))

        success = wx.SplitterWindow.SplitVertically(self, win1, win2, panewidth)
        return success
    #end def SplitVertically

    def GetExpectedSashPosition (self):
        # Use the parent pane's size and pane-to-pane proportion to calc the sash position.

        # The correction constants were found by experimentation using MSW.
        if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
            tot = max (self.GetMinimumPaneSize(), self.GetParent().GetClientSize().height - 6)
        else:
            tot = max (self.GetMinimumPaneSize(), self.GetParent().GetClientSize().width - 10)
        #end if

        return int (round (tot * self.proportion))
    #end def GetExpectedSashPosition

    def ResetSash (self):       # called by both OnResize and OnSashChanged
        self.SetSashPosition (self.GetExpectedSashPosition())   # expects an integer # of pixels
    #end def ResetSash

    def OnReSize (self, event):
        'Window has been resized, so we need to adjust the sash based on self.proportion.'

        wx.CallAfter (self.ResetSash)   # ResetSash --> SetSashPosition --> GetExpectedSashPosition
        event.Skip()
    #end def OnReSize

    def OnSashChanging (self, event):
        if self.sashlocked:
            event.Veto()
        else:
            event.Skip()
        #end if
    #end def (self, event):

    def OnSashChanged (self, event):
        'Recalculate self.proportion based on where user dragged the sash.'

        if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
            tot = max (self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
        else:
            tot = max (self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
        #end if

        posn = float (self.GetSashPosition())       # method of wx.ProportionalSplitter
        self.proportion = float (posn) / tot
        event.Skip()
    #end def OnSashChanged

    def OnPaint (self, event):
        # This method is called when the splitterwindow is first created and also when the sash is moved.

        if self.firstpaint:
            if self.GetSashPosition() != self.GetExpectedSashPosition():
                self.ResetSash()                    # ResetSash --> GetExpectedSashPosition
            #end if
            self.firstpaint = False
        #end if

        event.Skip()
    #end def OnPaint

#end class ProportionalSplitter

class MainFrame (wx.Frame):

    def __init__ (self, parent, id=-1, title='Default Frame Title', size=(500, 400), pos=(200, 150)):

        wx.Frame.__init__ (self, parent, id, title=title, size=size, pos=pos)
        self.frame = self

        # These are instantiated here as a reminder of what frame attributes exist.
        self.inpfilename, self.inpdirname, self.inpfilepathname = (None, None, None)
        self.outfilename, self.outdirname, self.outfilepathname = (None, None, None)
        self.statusbar, self.statustext = None, None
        self.panel1A, self.panel1B, self.panel2A, self.panel2B = None, None, None, None
        self.panel1Acolor, self.panel1Bcolor, self.panel2Acolor, self.panel2Bcolor = None, None, None, None
        self.outimagetypeList, self.outimageformatList = None, None
        self.preserveTrans, self.jpegquality = None, None
        self.outfiletype, self.outimageformat = None, None
        self.tcfilename, self.tcimageformat, self.tcimagemode, self.tctrans, self.tctranstype = None, None, None, None, None
        self.inpformat, self.inpmode, self.inptrans, self.inptranstype = None, None, None, None
        self.hasalpha, self.alpha256bit = None, None

        # Create a menu and a statusbar
        self.FileMenuStatusBar()
        self.statusbar.SetStatusText ('No Image File Currently Opened')

        # Create the 4 panes+panels
        self.SplitterWindows()      # create panel1A, panel1B, panel2A, panel2B

        # Create the controls that show the input image file's parameters
        self.Panel1Alayout (self.panel1A)

        # Create the controls that set the output image file's parameters
        self.Panel1Blayout (self.panel1B)

        self.Show (True)

    #end def __init__

    def Panel1Blayout (self, panel1B):

        self.outimagetypeList = ['BMP', 'EPS', 'GIF', 'IM', 'JPG', 'MSP', 
                                 'PCX', 'PDF', 'PNG', 'PPM', 'TIF', 'XBM']
        idRbouttype = wx.NewId()
        numcols = 4
        rbouttype = wx.RadioBox (panel1B, idRbouttype, 'Output File Image Type', (45, 10), 
                                 wx.DefaultSize, self.outimagetypeList, numcols, wx.RA_SPECIFY_COLS)
        #rbouttype.SetToolTip (wx.ToolTip ('Output File Image Type'))
        #rbouttype.SetLabel ('Alternate Label')
        rbouttype.SetBackgroundColour (self.panel1Bcolor)
        wx.EVT_RADIOBOX (self, idRbouttype, self.EvtRbouttype)
        self.outfiletype = self.outimagetypeList [0]       # default selection

        self.outimageformatList = ['1', 'L', 'P', 'RGB', 'JPG', 'CMYK']
        idRboutformat = wx.NewId()
        numcols = 4
        rboutformat = wx.RadioBox (panel1B, idRboutformat, 'Output Image Format', (45, 100), 
                                 wx.DefaultSize, self.outimageformatList, numcols, wx.RA_SPECIFY_COLS)
        #rboutformat.SetToolTip (wx.ToolTip ('Output Image Format'))
        #rboutformat.SetLabel ('Alternate Label')
        rboutformat.SetBackgroundColour (self.panel1Bcolor)
        wx.EVT_RADIOBOX (self, idRboutformat, self.EvtRboutformat)
        self.outimageformat = self.outimageformatList [0]     # default selection

        idPresTrans = wx.NewId()
        cbPresTrans = wx.CheckBox (panel1B, idPresTrans, 'Try to Preserve Transparency if Present', 
                                   wx.Point(45, 170), wx.DefaultSize, wx.NO_BORDER)
        cbPresTrans.SetBackgroundColour (self.panel1Bcolor)
        cbPresTrans.SetValue (True)
        wx.EVT_CHECKBOX (self, idPresTrans, self.EvtPresTrans)

        text = wx.StaticText (panel1B, -1, 'JPEG Image Quality', wx.Point(45, 193))
        self.tcJpegquality = wx.TextCtrl (panel1B, -1, '100', wx.Point (150, 190), 
                                          wx.Size(30, -1), style=wx.TE_READONLY)
        idJpegquality = wx.NewId()
        spJpegquality = wx.SpinButton (panel1B, idJpegquality, wx.Point(180, 190), 
                                       wx.Size(20, 20), wx.SP_VERTICAL)
        spJpegquality.SetRange (1, 100)
        spJpegquality.SetValue (100)
        wx.EVT_SPIN (self, idJpegquality, self.OnJpegquality)

        panel1B.Layout()

    #end def Panel1Blayout

    def OnFileSaveAs (self, event):
        """ File|SaveAs event - Prompt for File Name. """

        if self.inpfilepathname != None:

            # Use the input file's directory and filename with the chosen extension
            filename, ext = os.path.splitext (self.inpfilename)

            defaultfilepathname = filename + '.' + self.outfiletype

            dlg = wx.FileDialog (self, "Save As", self.inpdirname, defaultfilepathname,
                                 "*.*", wx.SAVE|wx.CHANGE_DIR)
    
            if (dlg.ShowModal() == wx.ID_OK):
                self.outdirname = dlg.GetDirectory()
                self.outfilename = dlg.GetFilename()
                self.outfilepathname = os.path.join (self.outdirname, self.outfilename)

                # Do the conversion and save
                #################################

                print 'OnFileSaveAs:    ', self.outfilepathname
            #end if

            dlg.Destroy()
        else:
            print 'OnFileSaveAs:    Cannot save to file: No input file has been chosen.'

            dlg = wx.MessageDialog (self.frame, 'Cannot save to file:  No input file has been chosen.',
                          'Cannot save', wx.OK | wx.ICON_INFORMATION)
                          # wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()

        #end if
    #end def OnFileSaveAs

    def OnJpegquality (self, event):

        self.tcJpegquality.SetValue (str (event.GetPosition()))
        self.jpegquality = event.GetPosition()

        print event.GetPosition()
    #end def

    def EvtPresTrans (self, event):

        print 'EvtCheckBox:', event.Checked()

        self.preserveTrans = event.Checked()
    #end def

    def EvtRboutformat (self, event):

        self.outimageformat = self.outimageformatList [event.GetInt()]

        print 'EvtRboutformat:    self.outimageformat', self.outimageformat
    #end def EvtRboutformat

    def EvtRbouttype (self, event):

        self.outfiletype = self.outimagetypeList [event.GetInt()]      # set file ext and type

        print 'EvtRbouttype:    self.outfiletype =', self.outfiletype
    #end def EvtRbouttype

    def Panel1Alayout (self, panel1A):

        txtfilename = wx.StaticText (panel1A, -1, "Current Input Image File", (10, 8))
        self.tcfilename = wx.TextCtrl (panel1A, -1, ' (No Image File Open)', (135, 5), 
                                       wx.Size(150, -1), style=wx.TE_READONLY)

        tclen = 50
        txtfiletype = wx.StaticText (panel1A, -1, "Input Image File Type", (10, 38))
        self.tcimageformat = wx.TextCtrl (panel1A, -1, '', (135, 35), 
                                          wx.Size(tclen, -1), style=wx.TE_READONLY)

        txtimagedata = wx.StaticText (panel1A, -1, "Image Data Type", (10, 63))
        self.tcimagemode = wx.TextCtrl (panel1A, -1, '', (135, 60), 
                                        wx.Size(tclen, -1), style=wx.TE_READONLY)

        txttrans = wx.StaticText (panel1A, -1, "Image Transparency", (10, 88))
        self.tctrans = wx.TextCtrl (panel1A, -1, '', (135, 85), 
                                    wx.Size(tclen, -1), style=wx.TE_READONLY)

        txtttranstype = wx.StaticText (panel1A, -1, "Transparency Type", (10, 113))
        self.tctranstype = wx.TextCtrl (panel1A, -1, '', (135, 110), 
                                         wx.Size(tclen, -1), style=wx.TE_READONLY)

        #sizerFilename = wx.BoxSizer (wx.HORIZONTAL)
        #relweight = 0
        #sizerFilename.Add (txtfilename, relweight, wx.ALL)      # COMMENT THIS OUT
        #sizerFilename.Add (self.tcfilename, relweight, wx.ALL)  # COMMENT THIS OUT

        ##sizerFilename.AddWindow (txtfilename, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
        ##sizerFilename.AddWindow (self.tcfilename, 0, wx.ALIGN_CENTRE|wx.ALL, 5)

        #self.SetSizer (sizerFilename)                           # COMMENT THIS OUT

        ##self.SetAutoLayout (True)
        ##self.Layout()

        ##self.SetAutoLayout (True)
        ##self.SetSizerAndFit (sizerFilename)    # reduces sizes of everything to their minimums
        ##sizerFilename.SetSizeHints (self)

        panel1A.Layout()

    #end def Panel1Alayout

    def FileMenuStatusBar (self):

        # Create the File menu
        self.statusbar = self.CreateStatusBar()

        # Setting up the menu.
        filemenu= wx.Menu()

        idOpen = wx.NewId()
        filemenu.Append (idOpen, "&Open"," Open an Image File")
        wx.EVT_MENU (self, idOpen, self.OnOpen)

        filemenu.AppendSeparator()

        idSaveas = wx.NewId()
        filemenu.Append (idSaveas, "Save &As"," Save an Image File")
        wx.EVT_MENU (self, idSaveas, self.OnFileSaveAs)

        filemenu.AppendSeparator()

        idAbout = wx.NewId()
        filemenu.Append (idAbout, "&About"," Information about this program")
        wx.EVT_MENU (self, idAbout, self.OnAbout)

        filemenu.AppendSeparator()

        idExit = wx.NewId()
        filemenu.Append (idExit,"E&xit"," End this program")
        wx.EVT_MENU (self, idExit, self.OnExit)

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append (filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar (menuBar)  # Adding the MenuBar to the Frame content.

    #end def FileMenuStatusBar

    def OnOpen (self, event):       # Open a file

        # 'wx.CHANGE_DIR' is necessary to return a full pathname in Win98
        dlg = wx.FileDialog (self, "Choose an Image File",
                             style=wx.OPEN|wx.FILE_MUST_EXIST|wx.CHANGE_DIR, wildcard='*.*')

        if dlg.ShowModal() == wx.ID_OK:
            self.inpfilename = dlg.GetFilename()
            self.inpdirname = dlg.GetDirectory()
            self.inpfilepathname = os.path.join (self.inpdirname, self.inpfilename)

            # Check if the input file is in any recognizable image file.

            try:
                self.inpIm = Image.open (self.inpfilename)
                fileOK = True
            except IOError:
                fileOK = False
            #end try

        #end if dlg.ShowModal()

        dlg.Destroy()

        if fileOK:
            self.tcfilename.SetValue (' ' + self.inpfilename)

            # Update the statusbar with the filename and its path.
            self.statustext = 'File:  ' + self.inpfilename + '  in  ' + self.inpdirname
            self.statusbar.SetStatusText (self.statustext)

            # Find and display the input image file's parameters.

            # BMP, PNG, TIFF, etc.
            self.inpformat = self.inpIm.format
            self.tcimageformat.SetValue (' ' + self.inpIm.format)

            # P, RGB, etc
            self.inpmode = self.inpIm.mode
            self.tcimagemode.SetValue (' ' + self.inpIm.mode)

            # Determine the image's transparency characteristics
            # RGB:  Only one alpha which = 255
            # PA    Only two alpha values, 0 and 255
            # RGBA: More than 2 alpha values
            self.hasalpha, self.alpha256bit = self.ExamineAlpha (self.inpIm)

            if self.hasalpha:
                self.tctrans.SetValue (' Yes')
                if self.alpha256bit:
                    self.tctranstype.SetValue (' 256-bit')
                else:
                    self.tctranstype.SetValue (' on/off')
                #end if
            else:
                self.tctrans.SetValue (' None')
                self.tctranstype.SetValue (' n/a')
            #end if

            # Get the dimensions of the panel
            extentsize = (288, 250)             # Hack !

            # Read the image file, rescale it to the panel extent and return the scaled wx.Bitmap
            imScaled, thumbnailsize = ImScaledFromFile (self.inpfilename, extentsize)

            xsize, ysize = thumbnailsize
            xextent, yextent = extentsize
            xoff = max (0, ((xextent - xsize) / 2))
            yoff = max (0, ((yextent - ysize) / 2))

            # For display purposes make the working image RGB
            imConverted = imScaled.convert ('RGB')

            # Create a new PIL image the size of the extent and fill it with the background color.
            imDisplay = Image.new ('RGB', extentsize)
            imDisplay.paste (self.panel2Acolor, (0, 0, xextent, yextent))

            # Now place the image in the center of the background image.
            imDisplay.paste (imConverted, (xoff, yoff))

            # Convert the scaled, bordered Pil image to a bitmap to be installed it into a static bitmap
            bitmap = imageconverts.Pil2Bitmap (imDisplay)

            # Create a window to put the bitmap in
            win2A = wx.Window (self.panel2A, -1, wx.Point(0, 0), extentsize)
            staticBitmap2A = wx.StaticBitmap (win2A, -1, wx.EmptyBitmap(extentsize[0], extentsize[1]))
            staticBitmap2A.SetBitmap (bitmap)

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


        else:
            # Popup to inform
            dlg = wx.MessageDialog (self.frame, 'Cannot open file:  Not a recognizable image file.',
                          'Not an Image File', wx.CANCEL | wx.ICON_INFORMATION)
                          # wx.OK | wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()

            # display in status bar
            statustext = 'Unrecognized File Type:  ' + self.inpfilename + '  in  ' + self.inpdirname
            self.statusbar.SetStatusText (statustext)
        #end if

    #end def OnOpen

    def ExamineAlpha (self, imUnknown):

        if imUnknown.mode != 'RGBA':
            imRgba = imUnknown.convert ('RGBA')
        else:
            imRgba = imUnknown
        #end if

        self.statusbar.SetStatusText ('Examining Image file ...')
        rgbaDatalist = list (imRgba.getdata())    # must make a list of tuples out of the special sequence
        self.statusbar.SetStatusText (self.statustext)

        # Image has active alpha if a != 255.
        # Image alpha type is variable if:
        #   1) any 2 different colors have active color, or
        #   2) any 2 active alpha values are != to each other

        hasalpha = False
        alphalist = []
        numalpha = 0
        alpha256bit = False
        for rgbatuple in rgbaDatalist:
            r, g, b, a = rgbatuple 

            if a != 255:                # check for activa alpha
                hasalpha = True
                if not rgbatuple in alphalist:
                    alphalist.append (rgbatuple)
                    numalpha += 1

                    if numalpha > 1:   # more than 1 color has alpha or same color has different alpha
                        alpha256bit = True
                        break
                    #end if
                #end if
            #end if
        #end for

        return (hasalpha, alpha256bit)

    #end def ExamineAlpha

    def OnAbout (self, event):      # Create a message dialog box
        dlg = wx.MessageDialog (self, 'An image file conversion program using PIL and wxPython', 
                                ' About Image Convert', wx.OK)
        dlg.ShowModal()             # Show the dialog
        dlg.Destroy()               # make it go away now that it's finished.
    #end def

    def OnExit (self, event):
        self.Close (True)           # Close the application.
    #end def

    def SplitterWindows (self):

        mainSplitr = ProportionalSplitter (self, wx.NewId(), proportion=0.5, minsize=250)
        mainSplitr.sashlocked = True

        # Define the contents of the toplevel splitter.
        subSplitr1 = ProportionalSplitter (mainSplitr, wx.NewId(), proportion=0.5)
        subSplitr1.sashlocked = True
        subSplitr2 = ProportionalSplitter (mainSplitr, wx.NewId(), proportion=0.5)
        subSplitr2.sashlocked = True

        mainSplitr.SplitHorizontally (subSplitr1, subSplitr2)

        # Define each subsplitter's contents.
        self.panel1A = wx.Panel (subSplitr1, -1)
        self.panel1Acolor = (245, 235, 225)
        self.panel1A.SetBackgroundColour (self.panel1Acolor)

        self.panel1B = wx.Panel (subSplitr1, -1)
        self.panel1Bcolor = (225, 245, 235)
        self.panel1B.SetBackgroundColour (self.panel1Bcolor)
        subSplitr1.SplitVertically (self.panel1A, self.panel1B)

        self.panel2A = wx.Panel (subSplitr2, -1)
        self.panel2Acolor = (225, 235, 245)
        self.panel2A.SetBackgroundColour (self.panel2Acolor)

        self.panel2B = wx.Panel (subSplitr2, -1)
        self.panel2Bcolor = (245, 245, 225)
        self.panel2B.SetBackgroundColour (self.panel2Bcolor)
        subSplitr2.SplitVertically (self.panel2A, self.panel2B)

    #end def SplitterWindows

#end class MainFrame

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

if __name__ == '__main__':

    myApp = wx.PySimpleApp()
    myFrame = MainFrame (None, -1, 'Image File Conversion Utility', size=(600, 600), pos=(200, 50))
    myApp.MainLoop()

#end if

