# -*- coding: utf-8 -*-

#	Programmer:	0.707	
#   $Id: py2exe_gui.pyw

__VER__  =  u'0.04'

import sys
import os
import wx
import  wx.grid as  gridlib

from  py2exe.build_exe import py2exe
from distutils.core import Distribution

exe_typs = ['console', 'windows','service','com_server']

def MakeExe(dir,par_d,opt_d):    
    os.chdir(dir)
    sys_old_path = sys.path[:]  
    sys.path.insert(0,dir)
    
    dist = Distribution(par_d) 
    cmd = py2exe(dist)
        
    cmd.__dict__.update(opt_d) 

    cmd.ensure_finalized() 
    
         
    cmd.run()        
    sys.path = sys_old_path        
        
    build_dir = os.path.join(dir,'build') 
    from distutils import   dir_util
    dir_util.remove_tree(build_dir)
 
class PyFileGrid(gridlib.Grid):
    def __init__(self, parent):
        gridlib.Grid.__init__(self, parent, -1,style=wx.STATIC_BORDER)
        self.CreateGrid(1, 2)             
        self.SetColLabelValue(0,u"文件名")
        self.SetColLabelValue(1,u"程序类型")
        self.SetColSize(0,200)
        self.SetColSize(1,100)
        self.SetRowLabelSize(30)
        self.DeleteRows()  
              
    def CreateEditor(self):
        return gridlib.GridCellChoiceEditor(exe_typs, False)        
        
    def SetValues(self,files): 
        row_c = self.GetNumberRows()
        if row_c:
            self.DeleteRows(0,row_c)
        file_c = len(files)
        self.AppendRows(file_c)
        for row,file in enumerate(files):
            self.SetCellEditor(row, 1, self.CreateEditor())
            self.SetCellValue(row,0,file)
            ext = os.path.splitext(file)[1].lower()
            if ext =='.pyw':
                self.SetCellValue(row,1,'windows')
            elif ext =='.py':
                self.SetCellValue(row,1,'console')
                
    def GetValueDict(self):        
        row_c = self.GetNumberRows()
        v_d = {}        
        for row in range(row_c):
            typ = self.GetCellValue(row,1)
            if typ in exe_typs:
                if typ not in v_d:
                    v_d[typ] = []
                v_d[typ].append(self.GetCellValue(row,0))
        return v_d
            
            


class DataFileGrid(gridlib.Grid):
    def __init__(self, parent):
        gridlib.Grid.__init__(self, parent, -1,style=wx.STATIC_BORDER)
        self.CreateGrid(1, 2)             
        self.SetColLabelValue(0,u"源文件")
        self.SetColLabelValue(1,u"安装目录")
        self.SetColSize(0,300)
        self.SetColSize(1,60)
        self.SetRowLabelSize(20)
        self.DeleteRows() 
        self.SetSelectionMode(1)
        
    def SetValues(self,paths):
        row_c = self.GetNumberRows()           
        file_c = len(paths)            
        self.AppendRows(file_c)
            
        for i,p in enumerate(paths):
            self.SetCellValue(row_c+i,0,p)
            self.SetCellValue(row_c+i,1,'data') 
            
    def DelSelectedRows(self):       
        tls = self.GetSelectionBlockTopLeft()
        brs = self.GetSelectionBlockBottomRight()

        needdel_rows = []
        for t,b in [(x[0],y[0]) for x,y in zip(tls,brs)]:
            needdel_rows += range(t,b+1)
        
        old_row_c = self.GetNumberRows()
        new_row_c = old_row_c-len(needdel_rows)
        
        remain_rows = []
        for row in  range(old_row_c):
            if row not in needdel_rows:
                remain_rows.append((self.GetCellValue(row,0),self.GetCellValue(row,1)))
        
        self.DeleteRows(0,old_row_c)
        self.AppendRows(new_row_c)        
        
        for i,fd in enumerate(remain_rows):
            self.SetCellValue(i,0,fd[0])
            self.SetCellValue(i,1,fd[1])
    
    def GetValueDict(self):
        row_c = self.GetNumberRows()
        v_d = {}        
        for row in range(row_c):
            path = self.GetCellValue(row,1).encode('utf-8')
            file = self.GetCellValue(row,0).encode('utf-8')
            if path not in v_d:
                v_d[path] = []
            if len(file):
                v_d[path].append(file)
        return v_d 

class MyFrame(wx.Frame):
    def __init__(self,title):
        wx.Frame.__init__(self,None,-1,title,style=wx.SYSTEM_MENU|wx.CAPTION|wx.CLOSE_BOX ,size=wx.Size(400,400))
     
        # Prepare the menu bar
        menuBar = wx.MenuBar()

        # 1st menu from left
        menu = wx.Menu()
        
        #menu.Append(101, u"打开", u"打开一个已有工程")
        #menu.Append(102, u"保存", u"把当前工程信息存盘")
        #menu.Append(103, u"另存为...", u"把当前工程信息换名存盘")
        menu.AppendSeparator()
        menu.Append(104, u"退出", u"退出本程序")
        # Add menu to the menu bar
        menuBar.Append(menu, u"工程")
        menu = wx.Menu()
        
        menu.Append(201, u"生成可执行文件", u"")
        menuBar.Append(menu, u"py2exe")
        
        
        self.SetMenuBar(menuBar)
        
        self.Bind(wx.EVT_MENU, self.Exit, id=104)
        self.Bind(wx.EVT_MENU, self.OnMakeExe,id=201 )
        
        #notebook
        self.nb = nb = wx.Notebook(self,-1,style=wx.NB_BOTTOM )

        #页面1,基本信息,选择要生成的控制台程序和窗口程程序脚本
        self.panel_1 = panel_1 = wx.Panel(nb, -1,style=wx.STATIC_BORDER)        
        self.nb.AddPage(panel_1,u"程序文件",select = True)        
  
           
        self.help_label = wx.StaticText(panel_1, -1, u"  点右边按扭选择要打包的python文件(可多选),再点菜单'py2exe'->'生成可执行文件'既可.",size=(240,50))
        self.add_py_btn = wx.Button(panel_1,-1, u"选择python文件",size=(100,40))      

        self.dir_label = wx.StaticText(panel_1, -1, u"目录:")
        self.dir_text = wx.TextCtrl(panel_1, -1, u"",style=wx.TE_READONLY )
                
        self.py_grid = PyFileGrid(self.panel_1)
        
        self.Bind(wx.EVT_BUTTON, self.AddPyFile,self.add_py_btn)    
        
         #页面3,数据文件
        self.panel_3 = panel_3 = wx.Panel(nb, -1,style=wx.STATIC_BORDER)        
        self.nb.AddPage(panel_3,u"数据文件")

        self.add_data_btn = wx.Button(panel_3,-1, u"添加",size=(40,24))
        self.del_data_btn = wx.Button(panel_3,-1, u"删除",size=(40,24))
        
        self.data_grid = DataFileGrid(self.panel_3)
        
        self.Bind(wx.EVT_BUTTON, self.AddDataFile,self.add_data_btn)
        self.Bind(wx.EVT_BUTTON, self.DelDataFile,self.del_data_btn)
        
        #页面2,选项
        self.panel_2 = panel_2 = wx.Panel(nb, -1,style=wx.STATIC_BORDER )
        self.nb.AddPage(panel_2,u"py2exe选项")
        
        self.optimize_label = wx.StaticText(panel_2, -1, u"optimization level(优化选项):")       
        self.optimize_box = wx.ComboBox(panel_2,-1,value='0',choices = ['0','1','2'],style = wx.CB_READONLY )
        
        self.distdir_label = wx.StaticText(panel_2, -1, u"dist-dir(存放生成文件的目录):")       
        self.distdir_box = wx.TextCtrl(panel_2,-1,u'dist',size=(200,22))
        
        self.excludes_label = wx.StaticText(panel_2, -1, u'excludes(排除的模块,以","分隔):')
        self.excludes_box = wx.TextCtrl(panel_2,-1,u'',size=(183,22))
        
        self.dllexcludes_label = wx.StaticText(panel_2, -1, u'dll-excludes(排除dll文件,以","分隔):')
        self.dllexcludes_box = wx.TextCtrl(panel_2,-1,u'',size=(168,22))
        
        
        self.ignores_label = wx.StaticText(panel_2, -1, u'ignores(忽略的模块,以","分隔):')       
        self.ignores_box = wx.TextCtrl(panel_2,-1,u'',size=(190,22))
        
        self.includes_label = wx.StaticText(panel_2, -1, u'includes(包含的模块,以","分隔):')       
        self.includes_box = wx.TextCtrl(panel_2,-1,u'',size=(185,22))
        
        self.packages_label = wx.StaticText(panel_2, -1, u'packages(包含的模块包,以","分隔):')       
        self.packages_box = wx.TextCtrl(panel_2,-1,u'',size=(168,22))
        
        self.bundle_label = wx.StaticText(panel_2, -1, u'bundle-files(打包dll级别):')       
        self.bundle_box = wx.ComboBox(panel_2,-1,value='3',choices = ['1','2','3'],style = wx.CB_READONLY )
        
        
        self.compressed = wx.CheckBox(panel_2,-1,u"compressed(生成压缩文件zipfile)")
        self.xref = wx.CheckBox(panel_2,-1,u"xref(生成模块交叉引用)")
        self.ascii = wx.CheckBox(panel_2,-1,u"ascii(不自动包含encoding和codecs)")


        
        #页面3,py2exe输出
        self.output = wx.TextCtrl(nb, -1,style=wx.TE_MULTILINE)
        self.nb.AddPage(self.output,u"py2exe输出")
        

        self.__do_layout()

        self.Bind(wx.EVT_CLOSE,self.OnCloseWindow,self) 

        self.lasts = ''
  
    def __do_layout(self):
       
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        sizer_0 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_1 = wx.BoxSizer(wx.HORIZONTAL)        
        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_11 = wx.BoxSizer(wx.VERTICAL)        
        sizer_12 = wx.BoxSizer(wx.VERTICAL)       
        
        
        sizer_0.Add(self.dir_label,0, wx.LEFT|wx.RIGHT, 5)        
        sizer_0.Add(self.dir_text, 1,wx.RIGHT|wx.EXPAND, 10)
             
       
        
        sizer_1.Add(self.py_grid,1, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 10)  
       
        
        sizer_2.Add(self.help_label, 0, wx.LEFT|wx.RIGHT,10)
        sizer_2.Add(self.add_py_btn, 1, wx.LEFT|wx.RIGHT,10)
        
        sizer.Add(sizer_2, 0,wx.TOP|wx.BOTTOM,20)
        sizer.Add(sizer_0, 0,wx.TOP|wx.EXPAND,10)
        sizer.Add(sizer_1, 1,wx.TOP|wx.EXPAND,10)
           
        
        self.panel_1.SetAutoLayout(1)
        self.panel_1.SetSizer(sizer)       
        
        
        
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        

        sizer1_1 = wx.BoxSizer(wx.HORIZONTAL)        
        sizer1_2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1_3 = wx.BoxSizer(wx.HORIZONTAL)        
        sizer1_31 = wx.BoxSizer(wx.HORIZONTAL)        
        sizer1_4 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1_5 = wx.BoxSizer(wx.HORIZONTAL)        
        sizer1_6 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1_7 = wx.BoxSizer(wx.HORIZONTAL)        
        sizer1_8 = wx.BoxSizer(wx.HORIZONTAL)
        
        sizer1_1.Add(self.optimize_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_1.Add(self.optimize_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_2.Add(self.distdir_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_2.Add(self.distdir_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_3.Add(self.excludes_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_3.Add(self.excludes_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_31.Add(self.dllexcludes_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_31.Add(self.dllexcludes_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_4.Add(self.ignores_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_4.Add(self.ignores_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_5.Add(self.includes_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_5.Add(self.includes_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_6.Add(self.packages_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_6.Add(self.packages_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_7.Add(self.bundle_label,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_7.Add(self.bundle_box,1, wx.LEFT|wx.RIGHT, 5) 
        
        sizer1_8.Add(self.compressed,0, wx.LEFT|wx.RIGHT, 5)  
        sizer1_8.Add(self.xref,0, wx.LEFT|wx.RIGHT, 5) 
        
        
        
        sizer1.Add(sizer1_1, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_2, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_3, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_31, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_4, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_5, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_6, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_7, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(sizer1_8, 0,wx.TOP|wx.BOTTOM,5)
        sizer1.Add(self.ascii,0, wx.LEFT|wx.RIGHT, 5) 
        self.panel_2.SetAutoLayout(1)
        self.panel_2.SetSizer(sizer1)
        
        
        sizer2 = wx.BoxSizer(wx.VERTICAL)
        sizer2_1 = wx.BoxSizer(wx.HORIZONTAL) 
        
        sizer2_1.Add(self.add_data_btn, 1, wx.LEFT|wx.RIGHT,50)
        sizer2_1.Add(self.del_data_btn,1, wx.LEFT|wx.RIGHT,50)
                   
        sizer2.Add(sizer2_1, 0, wx.TOP|wx.BOTTOM,10)
        sizer2.Add(self.data_grid,1,wx.EXPAND,0)
         
        self.panel_3.SetAutoLayout(1)
        self.panel_3.SetSizer(sizer2)
        
        
        
    def AddDataFile(self,evt):
        dlg = wx.FileDialog(
            self, message=u"选择文件", defaultDir="",wildcard=u"数据文件(*.*)|*.*",
            style=wx.OPEN | wx.CHANGE_DIR|wx.MULTIPLE
            )
       
        if dlg.ShowModal() == wx.ID_OK:
            self.data_grid.SetValues(dlg.GetPaths())
        dlg.Destroy()
    
    def DelDataFile(self,evt):
        self.data_grid.DelSelectedRows()

    
    def AddPyFile(self,evt):
        dlg = wx.FileDialog(
            self, message=u"选择文件", defaultDir="",wildcard=u"Python文件(*.py,*.pyw)|*.py;*.pyw",
            style=wx.OPEN | wx.CHANGE_DIR|wx.MULTIPLE
            )
       
        if dlg.ShowModal() == wx.ID_OK:
            self.dir_text.SetValue(dlg.GetDirectory())                        
            self.py_grid.SetValues(dlg.GetFilenames())         
                                          
        dlg.Destroy()
    
    
    def OnMakeExe(self,evt):
        self.output.Clear()
        self.nb.SetSelection(3)
        self.lasts = ''
        
        dir = self.dir_text.GetValue()
        par_d = self.GetPar()
        opt_d = self.GetOpt()
        
        MakeExe(dir,par_d,opt_d)
       
        
    def GetPar(self):
        par_d = {}
        par_d["zipfile"] = "library.zip"
        par_d["ctypes_com_server"]= []
        par_d["com_server"] = []
        par_d["service"] = []
        par_d["console"] =[]
        par_d["isapi"] = []
        par_d["windows"] = []
        par_d["console"] = []
        
        par_d.update(self.py_grid.GetValueDict())               
        par_d["data_files"] = self.data_grid.GetValueDict().items()        
        
        return par_d
    
    def GetOpt(self):
        opt_d = {}
         
        opt_d['optimize'] = self.optimize_box.GetValue().encode('utf-8')
        opt_d['bundle_files'] = self.bundle_box.GetValue().encode('utf-8')
        
        dist_dir = self.distdir_box.GetValue().encode('utf-8')
        if dist_dir != '':
            opt_d['dist_dir'] = dist_dir
        
        excludes = self.excludes_box.GetValue().encode('utf-8')
        if excludes != "":
            opt_d['excludes'] = excludes
        
        dllexcludes = self.dllexcludes_box.GetValue().encode('utf-8')
        if dllexcludes != "":
            opt_d['dll_excludes'] = dllexcludes
            
        ignores = self.ignores_box.GetValue().encode('utf-8')
        if ignores!= "":
            opt_d['ignores'] = ignores
        
        includes = self.includes_box.GetValue().encode('utf-8')
        if includes != "":
            opt_d['includes'] = includes
        
        packages = self.packages_box.GetValue().encode('utf-8')
        if packages != "":
            opt_d['packages'] = packages
        
        
        opt_d['compressed'] = int(self.compressed.GetValue())
        opt_d['xref'] = int(self.xref.GetValue())
        opt_d['ascii'] = int(self.ascii.GetValue())
        
        return opt_d
        
        
    def Exit(self,evt):
        self.Close(True)
          
    def OnCloseWindow(self, event):
        self.Destroy()
        
    def write(self,s):
        pre = s[:8]
        if pre=="byte-com" or pre=="skipping":
            return
        if s == self.lasts:
            return
        
        self.lasts = s
        self.output.AppendText(s)

# end of class MyFrame

class MyApp(wx.App):
    def OnInit(self):
        frame_1 = MyFrame(u'PY2EXE GUI  V'+__VER__)
        frame_1.Show(True)
        sys.stdout = frame_1
        #sys.stderr = frame_1
        self.SetTopWindow(frame_1)
        return True

if __name__ == "__main__":
    app = MyApp(0)        
    app.MainLoop()
