devwiki:python

Differences

This shows you the differences between two versions of the page.


Previous revision
devwiki:python [2024/03/25 06:36] (current) – [Python and interaction with other API] ying
Line 1: Line 1:
 +
 +
 +====== Modern Python Practice ======
 +
 +  * nowaday, now one use python 2.x anymore, use python 3 standard, use this
 +    * use pathlib instead of os.path
 +    * use f"{keyword}" instead of "{0}".format(keyword), unless it is easier
 +
 +
 +====== Online Python run ======
 +
 +  * https://jupyter.org/try-jupyter/retro/notebooks/?path=notebooks/Intro.ipynb
 +  * to install locally: <code>pip3 install --upgrade pip</code>
 +
 +====== Install related ======
 +===== install Python3 on mac =====
 +
 +  * Mac has its own python install, very old, only for basic usage
 +  * to install Python3 from official python website, 
 +    - download python 3 for mac installer
 +    - install as usual and done
 +    - run python3 in cmd
 +    - then it pops: "python3" command requires the "the commandline developer tools" to install, click install
 +    - if you already install XCode (the developer suite for mac), then no need; if not, you can just install the smaller "Command line tools" with your Apple account
 +    - go Apple developer downloads page, search command line tools for xcode, download that and install
 +
 +===== Manage and handle multiple versions of python case =====
 +
 +Situation:
 +  * sometimes, you may have to keep both python 2.7, python 3.x
 +  * and sometimes you want to try python with AI, and AI need to install tons of AI related python modules
 +  * sometimes you just to keep python module in the min form to test something
 +
 +Solution:
 +  * for those case, when install python, never add python to system path
 +  * just download and make python folder a portable folder (you can install and copy the python whole folder to other machine as a portable app, better zip to copy, faster)
 +  * then your computer cmd never know where is python and won't random get your python from somewhere
 +  * then, dynamically set python into python in cmd session or batch file, or use full path to python.exe to run your script
 +    * session based method (win as example) <code dos>SET PATH=D:\App\Python;D:\App_Dev\PortableGit\bin;%PATH%</code>
 +    * batch file method <code dos test.batch>
 +@echo off
 +SetLocal EnableDelayedExpansion
 +set CustPython=D:\App\Python\
 +call !CustPython!python.exe my_script.py
 +</code>
 +
 +
 +====== Python 3 Changes and How to Make 2.7 Code Works ======
 +
 +  * make print "" into print(""), like as a function<code>
 +# in code editor, using Regular Expression to replace
 +find: (print)\s(.+)
 +replace: $1\($2\) or \1(\2) depending on your code editor
 +</code>
 +  * other stuff <code python>
 +# python 2 reload
 +reload(myModule)
 +# python 3 reload
 +from imp import reload
 +reload(myModule)
 +
 +# python 2 execfile
 +execfile("testScript.py")
 +# python 3
 +exec(open("testScript.py").read(), globals())
 +</code>
 +
 +  * make compatible to python 3 <code python>
 +if sys.version_info[:3][0]>=3:
 +    import importlib
 +    reload = importlib.reload # add reload
 +    raw_input = input # add raw_input
 +    xrange = range # range
 +    long = int # int
 +    unicode = str # str
 +</code>
 +
 +  * integer division difference in py3 vs py2, a/b
 +    * ref: https://blog.finxter.com/how-to-divide-two-integers-in-python/
 +    * for safe, use <code>35/60 = 0 # py2
 +35//60  = 0 # py2 , always use // for calculate minutes and seconds
 +
 +35/60 = 0.583 #py3
 +35//60 = 0 # py3
 +</code>
 +
 +====== Python basics ======
 +
 +  * online python code runner: https://trinket.io/
 +  * Python download: http://www.python.org/
 +  * a cool python shell: http://www.dreampie.org/
 +
 +** My Wiki Page related**
 +  * how to publish Python script as standalone application: [[devwiki:pyapp|Python script App]]
 +  * My App code share: [[devwiki:pyapp:mypyapp|My Py App]]
 +  * My Python GUI code template: [[devwiki:template|My generic Qt GUI templates]]
 +
 +** to read: **
 +  * https://automatetheboringstuff.com/chapter9/
 +  * Code Like a Pythonista: Idiomatic Python http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
 +
 +===== Python vs . =====
 +
 +  * Python vs Swift
 +    * https://blog.michaelckennedy.net/2014/11/26/comparison-of-python-and-apples-swift-programming-language-syntax/
 +
 +===== Python Comment Tips =====
 +
 +  * single line comment <code python># single comment</code>
 +  * multiple line comment <code python>'''
 +line 1
 +line 2
 +'''
 +</code>
 +  * block, section, partition <code python>
 +
 +########################################
 +# a major block of asset (40x#)
 +########################################
 +    
 +#------------------------------
 +# a function collection (30x-)
 +#------------------------------
 +
 +# ---- section comment ----
 +
 +# function section tip
 +def ________Loading_Functions________():
 +    # 8x_
 +    pass
 +</code>
 +===== Launch Python script =====
 +
 +  * launch in windows command batch file next to it<code batch>@echo off
 +call python %~dp0\\MyPythonScript.py</code>
 +  * launch in Mac AppleScript file (wip)<code applescript>
 +set myScriptPath to path to me
 +set myScriptFolderPath to POSIX path of (container of myScriptPath as string) -- in "/" form
 +set myScriptName to name of myScriptPath
 +
 +tell application "Terminal"
 +    do script with command "python /path_to_script/myScript.py"
 +end tell
 +</code>
 +===== OS system operation =====
 +
 +  * get user/home folder <code python>
 +os.path.expanduser('~') # return home directory, in Maya it return document, in windows, it return user dir
 +os.getenv("HOME") # return home directory
 +os.getenv("USERPROFILE") # return user profile directory
 +</code>
 +
 +  * make python code able to print unicode directly without .encode('utf-8') for text <code python>import sys
 +import codecs
 +sys.stdout = codecs.getwriter('utf8')(sys.stdout)
 +</code>
 +  * get python program path<code python>import sys
 +print(sys.executable) # the python cmd or app containing the python call path
 +</code>
 +  * get python module path <code python>import ModuleName
 +os.path.dirname(ModuleName.__file__)
 +</code>
 +  * get script path <code python>os.path.dirname(__file__)</code>
 +  * get class path but not its super class <code python>
 +self.location = os.path.realpath(sys.modules[self.__class__.__module__].__file__)
 +self.location = os.path.abspath(inspect.getfile(self.__class__))
 +</code>
 +  * get class name <code python>self.__class__.__name__</code>
 +  * get basename of folder and file <code python>os.path.basename(os.path.normpath('/folderA/folderB/folderC/folderD/')) # 'folderD'</code>
 +  * get file name without extension <code python>
 +file_name = os.path.basename(file_path).rsplit('.',1)[0]
 +import pathlib
 +file_name_2 = pathlib.Path(file_path).stem
 +</code>
 +  * get expanded and un-symbolic path <code python>
 +os.path.abspath(__file__)
 +os.path.realpath(__file__)
 +</code>
 +  * path comparison <code python>
 +os.path.relpath(childPath, parentPath) # get the sub path only based on parent Path
 +os.path.commonprefix(list_of_paths) # get the common parent path of all input pathes
 +</code>
 +  * python code to detect platform, python version, bit <code python>
 +from sys import platform
 +
 +def get_platform():
 +    if platform.startswith('win'):
 +        return 'windows'
 +    elif platform.startswith('darwin'):
 +        return 'mac'
 +    return 'linux'
 +
 +import sys
 +pyMode = '.'.join([ str(n) for n in sys.version_info[:3] ])
 +print("Python: {0}".format(pyMode))
 +
 +is_64bits = sys.maxsize > 2**32
 +print(is_64bits)
 +</code>
 +  * check file and path exists<code python>os.path.exists(myPath):</code>
 +  * scan file and folder content with pattern filtering <code python>
 +result_list = [x for x in os.listdir(scanPath) if os.path.isdir(os.path.join(scanPath,x))] 
 +# isdir for folder, isfile for file
 +cur_pattern = re.compile('\d{3}_\d{4}') # match 000_0000 name pattern
 +result_list = [x for x in result_list if cur_pattern.match(x)]
 +
 +# jpg name example
 +jpg_list = [x.rsplit('.',1)[0] for x in os.listdir(scanPath) if os.path.isfile(os.path.join(scanPath,x)) and x.lower().endswith('.jpg')]
 +</code>
 +  * rename file or folder <code python>os.rename(os.path.join(parentPath,old_name), os.path.join(parentPath,new_name))</code>
 +  * move file (always use full path for safe, and always check source and target exist before operation)<code python>
 +# file move within same drive
 +os.rename("/sourcePath/fileName.ext", "/targetPath/fileName.ext")
 +os.replace("/sourcePath/fileName.ext", "/targetPath/fileName.ext")
 +# work for folder and file and cross disk drive
 +shutil.move("/sourcePath/fileName.ext", "/targetPath/fileName.ext")
 +</code>
 + 
 +  * system cmds for each os<code python>
 +os.system('move "%s\%s" "%s\zBk.%s"' % (rootDir,oldPreset,rootDir,oldPreset))
 +os.system('mklink /D "%s\%s" "%s\%s"' % (rootDir,oldPreset,resDir,newPreset))
 +</code>
 +  * cross-platform delivery from source
 +    * win: frozen
 +    * mac: frozen
 +    * lin: packaged
 +    * <code python>
 +def getSetup():
 +    if hasattr(sys, 'frozen'):
 +        return 'frozen'
 +    elif is_packaged():
 +        return 'packaged'
 +    return 'source'
 +</code>
 +  * change file permission <code python>
 +# ref: http://stackoverflow.com/questions/16249440/changing-file-permission-in-python
 +import os
 +import stat
 +os.chmod("R:\\folder\\permit.txt", stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) # user, group, other readonly
 +</code>
 +  * windows register operation <code python>
 +PS_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"SOFTWARE\\Adobe\\Photoshop\\12.0") # may need check version no.
 +PS_APP = _winreg.QueryValueEx(PS_key, 'ApplicationPath')[0] + 'Photoshop.exe'
 +
 +def ps_loc_win():
 +    import _winreg
 +    regKey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Photoshop.exe")
 +    regPath = _winreg.QueryValueEx(regKey, 'Path')[0] + 'Photoshop.exe'
 +    return regPath
 +</code>
 +
 +  * windows set default playback device <code python>subprocess.call('nircmd.exe setdefaultsounddevice "Speakers"') # need download nircmd</code>
 +
 +  * run python script itself as admin <code python>
 +import ctypes, sys
 +def is_admin():
 +    try:
 +        return ctypes.windll.shell32.IsUserAnAdmin()
 +    except:
 +        return False
 +
 +if is_admin():
 +    # Code of your program here
 +else:
 +    # Re-run the program with admin rights
 +    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1)
 +</code>
 +  * run cmd with admin (3rd party module) <code python>
 +# pywin32 : sourceforge.net/projects/pywin32/files/pywin32/
 +import win32com.shell.shell as shell
 +commands = 'echo hi'
 +shell.ShellExecuteEx(lpVerb='runas', lpFile='cmd.exe', lpParameters='/c '+commands)
 +</code>
 +
 +  * control, start, stop, restart service (3rd party module method) <code python>
 +import win32serviceutil
 +serviceName = "serviceName"
 +win32serviceutil.RestartService(serviceName)
 +</code>
 +
 +==== List File and sub-folders - the Correct Way ====
 +
 +  * in Python 3.5, scandir module is part of python, also inside os.walk() internal instead of os.listdir() old way.
 +  * so compare glob, python 2.7 os.walk() and scandir (also available as external module for previous python)
 +    * scandir is at least 10x faster than old os.walk, glob is 2x faster than old os.walk by skipping dir/file type checking
 +      * 132s (walk) / 62s (glob) / 12s (scandir)
 +
 +<code python>
 +import glob
 +def getDirData(top, maxDepth=1):
 +    res = []
 +    for d in range( 1, maxDepth+2 ):
 +        maxGlob = "/".join( "*" * d )
 +        topGlob = os.path.join( top, maxGlob )
 +        allFiles = glob.glob( topGlob )
 +        res.extend( allFiles )
 +        #someFiles.extend( [ f for f in allFiles if fnmatch.fnmatch( os.path.basename( f ), fnMask ) ] )
 +    return res
 +
 +import os
 +def getDirData( top, maxDepth=1):
 +    # ref: https://stackoverflow.com/questions/7159607/list-directories-with-a-specified-depth-in-python
 +    # py3 option: better with scandir, 
 +    # py2 has: https://github.com/benhoyt/scandir
 +    top = os.path.normpath(top)
 +    res = []
 +    root_len = len(top) #+len(os.path.sep)
 +    for root,dirs,files in os.walk(top, topdown=True):
 +        depth = root[root_len:].count(os.path.sep)
 +        if depth < maxDepth:
 +            res += [os.path.join(root, d) for d in dirs]
 +            res += [os.path.join(root, f) for f in files]
 +        elif depth == maxDepth:
 +            res += [os.path.join(root, d) for d in dirs]
 +            res += [os.path.join(root, f) for f in files]
 +            dirs[:] = [] # Don't recurse any deeper
 +    return res
 +
 +# detail inf: https://benhoyt.com/writings/scandir/
 +# can use with maya 2017 by zip out _scandir.pyd and scandir.py: (scandir-1.10.0-cp27-cp27m-win_amd64.whl) https://pypi.org/project/scandir/1.10.0/#files
 +tmp_path = r'D:\PyLib\scandir27x64'
 +tmp_path in sys.path or sys.path.append(tmp_path)
 +import scandir
 +def getDirData(top, maxDepth=1):
 +    # py3.5 has built in, only for 2.7
 +    # https://github.com/benhoyt/scandir
 +    top = os.path.normpath(top)
 +    res = []
 +    root_len = len(top)#+len(os.path.sep)
 +    for root,dirs,files in scandir.walk(top, topdown=True):
 +        depth = root[root_len:].count(os.path.sep)
 +        if depth < maxDepth:
 +            res += [os.path.join(root, d) for d in dirs]
 +            res += [os.path.join(root, f) for f in files]
 +        elif depth == maxDepth:
 +            res += [os.path.join(root, d) for d in dirs]
 +            res += [os.path.join(root, f) for f in files]
 +            dirs[:] = [] # Don't recurse any deeper
 +    return res
 +    
 +# test
 +import time
 +start = time.time()
 +result = getDirData(r'R:\ServerFolder\RootDir',5)
 +end = time.time()
 +print('used: {}s'.format(end - start))
 +</code>
 +  * other module option: https://github.com/juancarlospaco/faster-than-walk
 +
 +==== Copy File - the Correct Way ====
 +
 +  * speed on windows: SHFileOperation (8) > win32file (8.6) > os System (9) > shutil (18)
 +    * ref: http://www.cnblogs.com/lovemo1314/archive/2010/11/11/1874975.html
 +
 +  * use python shutil module copy, copyfile and copy2 function <code python>
 +import shutil
 +# copy: return target path, target can be file or folder
 +shutil.copy('d:/z_tmp/aa.jpg','d:/z_tmp/bb.jpg')
 +shutil.copyfile('d:/z_tmp/aa.jpg','d:/z_tmp/dd.jpg')
 +
 +''' # result modified date change
 +02/06/2017  02:13 PM           193,399 aa.jpg
 +12/09/2017  12:06 PM           193,399 bb.jpg
 +12/09/2017  12:06 PM           193,399 dd.jpg
 +'''
 +
 +shutil.copy2('d:/z_tmp/aa.jpg','d:/z_tmp/bb.jpg')
 +''' # result keeps modified date
 +02/06/2017  02:13 PM           193,399 aa.jpg
 +02/06/2017  02:13 PM           193,399 cc.jpg
 +'''
 +</code>
 +  * copy folder and its content <code python>
 +import shutil, errno
 +
 +def copyFolder(src, dst):
 +    try:
 +        shutil.copytree(src, dst) # folder structure only
 +    except OSError as exc: # python >2.5
 +        if exc.errno == errno.ENOTDIR:
 +            shutil.copy2(src, dst) # file only with same date, or copy for current date
 +        else: raise
 +        
 +# or use this
 +distutils.dir_util.copy_tree(src_folder, tgt_folder)
 +</code>
 +  * python more complete version for copy folder and its content <code python>
 +# ref: http://blog.csdn.net/liyuan_669/article/details/25346645
 +# folder content to target folder content. created folders seams not same date, but file same date
 +import os
 +import shutil
 +def copy_tree(src, dst, symlinks=False):
 +    names = os.listdir(src)
 +    if not os.path.isdir(dst):
 +        os.makedirs(dst)
 +    errors = []
 +    for name in names:
 +        srcname = os.path.join(src, name)
 +        dstname = os.path.join(dst, name)
 +        try:
 +            if symlinks and os.path.islink(srcname):
 +                linkto = os.readlink(srcname)
 +                os.symlink(linkto, dstname)
 +            elif os.path.isdir(srcname):
 +                copy_tree(srcname, dstname, symlinks)
 +            else:
 +                # perform overrides
 +                if os.path.isdir(dstname):
 +                    os.rmdir(dstname)
 +                elif os.path.isfile(dstname):
 +                    os.remove(dstname)
 +                shutil.copy2(srcname, dstname)
 +            # other case like: devices, sockets etc.
 +        except (IOError, os.error) as why:
 +            errors.append((srcname, dstname, str(why)))
 +        # catch the Error from the recursive copy_tree so that we can
 +        # continue with other files
 +        except OSError as err:
 +            errors.extend(err.args[0])
 +    try:
 +        shutil.copystat(src, dst)
 +    except WindowsError:
 +        # can't copy file access times on Windows
 +        pass
 +    except OSError as why:
 +        errors.extend((src, dst, str(why)))
 +    if errors:
 +        raise Error(errors)
 +
 +copy_tree('E:/book', 'E:/newbook')
 +</code>
 +  * use system command to copy <code python>
 +src = 'd:/z_tmp/aa.jpg'
 +tgt = 'd:/z_tmp/sys_copy.jpg'
 +os.system('copy {0} {1}'.format(os.path.normpath(src),os.path.normpath(tgt)) )
 +# normpath will change the slash for win from unix path
 +
 +os.system ("xcopy /s {0} {1}".format(src_dir, tgt_dir))
 +</code>
 +  * use system file manager to copy with system progress window (windows) <code python>
 +#ref: https://stackoverflow.com/questions/16867615/copy-using-the-windows-copy-dialog
 +import pythoncom
 +from win32com.shell import shell,shellcon
 +
 +def win_copy_process(src_files,tgt_folder):
 +    # win32com module
 +    pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)
 +    # Respond with Yes to All for any dialog
 +    # ref: http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
 +    pfo.SetOperationFlags(shellcon.FOF_NOCONFIRMATION)
 +    # Set the destionation folder
 +    dst = shell.SHCreateItemFromParsingName(tgt_folder,None,shell.IID_IShellItem)
 +    for f in src_files:
 +        src = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
 +        pfo.CopyItem(src,dst) # Schedule an operation to be performed
 +    # ref: http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
 +    success = pfo.PerformOperations()
 +    # ref: sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
 +    aborted = pfo.GetAnyOperationsAborted()
 +    return success and not aborted
 +
 +file_list = [r'D:\z_tmp\aa.jpg',r'D:\z_tmp\bb.jpg']
 +tgt_folder = r'D:\zTmp'
 +win_copy_process(file_list,tgt_folder)
 +</code>
 +  * use system API (windows) <code python>
 +#ref: http://timgolden.me.uk/python/win32_how_do_i/copy-a-file.html
 +# win32
 +from win32com.shell import shell, shellcon
 +win32file.CopyFile (filename1, filename2, 1)
 +
 +# win32com : with windows copy dialog
 +shell.SHFileOperation (  (0, shellcon.FO_COPY, filename1, filename2, 0, None, None) )
 +# for the "0" parameter: 
 +# shellcon.FOF_RENAMEONCOLLISION: rename on collision
 +# shellcon.FOF_NOCONFIRMATION: no confirmation dialog and always override
 +# shellcon.FOF_NOERRORUI: no error pop dialog
 +# shellcon.FOF_SILENT: no copying progress dialog
 +# shellcon.FOF_NOCONFIRMMKDIR: no ask for non exist folder, create folder by default
 +</code>
 +
 +===== string operation =====
 +
 +  * encode and decode Qt QString <code python>str(myQStringObj) # ascii output
 +unicode(myQStringObj) # unicode output
 +unicode(myQStringObj.toUtf8(), encoding="UTF-8") # decode based on utf8, then encode in utf-8 python string as unicode output
 +</code>
 +  * ref: 
 +    * UTF-8 is an encoding used to translate numbers into binary data. 
 +    * Unicode is a character set used to translate characters into numbers.
 +  * text block to clean row list <code python>
 +data_list = [row.strip() for row in source_txt.split('\n')]
 +</code>
 +
 +  * to use non-ascii character in python file, you need to put encoding in first line of code, or you will get "Non-ASCII character '\xe2' in file" error <code python>
 +# coding: utf-8
 +my_special_string = u"好"
 +print(u"好")
 +</code>
 +  * remove extra space from a string, also remove end single space<code python>
 +tidy_name = ' '.join(old_name.split())
 +
 +# just remove extra space
 +tidy_name = re.sub(' +', ' ', old_name) # only space, need strip() to clear end space
 +tidy_name = re.sub('\s{2,}', ' ', old_name) # space, tab, new line, need strip() to clear end space
 +</code>
 +=====  control flow operation =====
 +
 +  * define function <code python>
 +def my_fun(para1):
 +    print para1
 +</code>
 +  * logic and condition and loop operation <code python>for tmpitem in tmplist:
 +    print tmpitem
 +
 +if tmp > 0:
 +    print 'ok'
 +elif x == 0:
 +    print 'zero'
 +else:
 +    print 'no'
 +
 +# logic operation
 +x or y
 +x and y
 +not x
 +</code>
 +  * try operation, note try is not a block, variable is same scope before try <code python>
 +try:
 +    val = test(A)
 +except:
 +    pass
 +# ref: https://teamtreehouse.com/community/scope-of-a-variable  
 +</code>
 +===== data operation =====
 +
 +  * data type check <code python>
 +isinstance(Vehicle(), Vehicle)  # returns True
 +type(Vehicle()) == Vehicle      # returns True
 +isinstance(Truck(), Vehicle)    # returns True, takes inheritance
 +type(Truck()) == Vehicle        # returns False, take exact class type
 +
 +# check string, both python 2,3
 +isinstance(my_data, (str, unicode) )
 +# check list
 +isinstance(my_data, (list, tuple) )
 +
 +# check not None, note, None is its own type and has only one instance
 +my_vec != None # sometime, it may cause error when = try to read certain way
 +my_vec is not None # this way is preferred to be error free
 +isinstance(my_vec, om.MVector) # this way even better, not only check it is defined and also check its type match
 +
 +# compare type
 +if type(A) is type(B):
 +    print('Same Class')
 +    
 +# note : is for same object and like pointer comparision, while == is same value comparision
 +</code>
 +
 +  * data type convertion <code python>int('42')
 +str(42)
 +</code>
 +  * get datetime date <code python>date_text = datetime.date.today().strftime("%Y.%m.%d")</code>
 +  * get processing time <code python>
 +import time
 +
 +start = time.time()
 +print("hello")
 +end = time.time()
 +print(end - start)
 +</code>
 +  * wild char process <code python>
 +import glob
 +os.chdir(path)
 +for file in glob.glob("*.jpg"):
 +    print file
 +    
 +tmplist=glob.glob('*.[jJ][pP][gG]')
 +
 +# other method: fnmatch and re
 +</code>
 +  * string operation <code python>
 +name="01_Blue_Sun_DSC08897.jpg"
 +print name[3:-13].replace('_',' ') #Blue Sun#
 +
 +len(str) # string length
 +
 +"hello world".title()
 +'Hello World'
 +
 +# remove Whitespace on the both sides:
 +s = "  \t a string example\t  "
 +s = s.strip()
 +
 +# remove Whitespace on the right side:
 +s = s.rstrip()
 +# remove Whitespace on the left side:
 +s = s.lstrip()
 +</code>
 +
 +Data Type
 +  * Lists []: a list of values. Each one of them is numbered, starting from zero - the first one is numbered zero, the second 1, the third 2, etc. You can remove values from the list, and add new values to the end. Example: Your many cats' names.
 +  * Tuples (): are just like lists, but you can't change their values. The values that you give it first up, are the values that you are stuck with for the rest of the program. Again, each value is numbered starting from zero, for easy reference. Example: the names of the months of the year.
 +  * Dictionaries {'':'',}are similar to what their name suggests - a dictionary. In a dictionary, you have an 'index' of words, and for each of them a definition. In python, the word is called a 'key', and the definition a 'value'
 +
 +  * list operation (so-called array) ([[http://effbot.org/zone/python-list.htm|ref]]) <code python>
 +[1,2,3]
 +len(arrayName) # size of list
 +
 +# add value to all elements of list
 +my_list=[1,3,5,7,8]
 +new_list = [x+1 for x in my_list] # [2,4,6,8,9]
 +
 +list.append(obj)
 +list.extend(list)
 +list.index(obj)
 +list.count(obj) # count appearance of obj
 +
 +# array to string and string to array
 +sentence = ["there", "is", "no", "spoon"]
 +' '.join(sentence) #'there is no spoon'
 +
 +list=[3.1,121.2,34.1]
 +', '.join(map(str,list)) #'3.1, 121.2, 34.1'
 +
 +'3.1, 121.2, 34.1'.split(', ') #['3.1','121.2','34.1']
 +# 2nd number make how many split point
 +'3.1, 121.2, 34.1'.split(', ',1) #['3.1','121.2, 34.1'
 +
 +# list sort (modify itself)
 +versionList.sort(reverse=1) # higher to lower
 +versionList.sort() 
 +
 +str([-4, -2, 0, 2, 4]) # '[-4,-2,0,2,4]'
 +bb_str ='_'.join(map(str, bb)) # -4_-2_0_2_4
 +
 +# list compare
 +[-4, -2, 0, 2, 4] == [-4, -2, 0, 2, 3] # false
 +
 +# multiple list operation
 +
 +wMap_zero_Jnt=[x for x in wMap_All_Jnt if x not in wMap_Jnt_tgt] # remove list from list
 +
 +a = [1, 2, 3, 4, 5]
 +b = [9, 8, 7, 6, 5]
 +common = set(a) & set(b) #[5], no order applied
 +
 +[i for i, j in zip(a, b) if i == j] # [5] for equal-sized lists, which order matters 
 +
 +version_list_prefix=["previous","current"]
 +version_list_postfix=["A","B"]
 +version_list_color=["rgb(255, 128, 128)","rgb(85, 170, 255)"]
 +for each,post,color in zip(version_list_prefix,version_list_postfix,version_list_color):
 +    print(each+" "+post+" is "+color)
 +    
 +# list slice operation
 +# n = len(L)
 +# item = L[index]
 +# seq = L[start:stop]
 +# seq = L[start:stop:step]
 +seq = L[::2] # get every other item, starting with the first
 +seq = L[1::2] # get every other item, starting with the second
 +
 +seq=L[::-1] # reverse reading list
 +list(reversed(seq)) # generate a list reversed
 +L.reverse() # reverse list, it changed original list
 +seq=L[::-2] # reverse reading list, and get every other item
 +
 +list=[1,2,3,4,5,6]
 +seq=list[::-2] # [6,4,2]
 +
 +# list to pair list
 +info = ['name','John', 'age', '17', 'address', 'abc']
 +info_pair = zip(info[::2], info[1::2])
 +# [('name', 'John'), ('age', '17'), ('address', 'abc')] 
 +
 +# list flatten, and remove duplicates
 +nestList = [[2,3],[1,2,4],[5,6]]
 +flatList = set(sum(nestList,[])) # for small list of 2 level nest
 +
 +# delete a element from list
 +a = [1,2,3,4]
 +del(a[2]) # [1,2,4]
 +
 +# for large list
 +import itertools
 +flatList = set( list(itertools.chain.from_iterable(nestList)) ) # for same type element list, no mix of list and element like [[5],6], as it will error
 +
 +import operator
 +flatList = set( reduce(operator.add, map(list, nestList)) ) # like itertool, but it will not error for mix of list and element situation
 +
 +# for any mix list
 +def flatFn(mainList):
 +    newList = []
 +    for each in mainList:
 +        if isinstance(each, (list, tuple)):
 +            newList.extend( flatFn(each) )
 +        else:
 +            newList.append(each)
 +    return newList
 +    
 +flatList = set( flatFn(nestList) )
 +
 +
 +</code>
 +  * dictionary operation <code python>
 +# ref: http://www.dotnetperls.com/dictionary-python
 +dict.clear()
 +dict.get(key, default=None) #  returns value or default if key not in dictionary
 +dict.has_key(key)
 +dict.items() # a list dict's (key, value) tuple pairs
 +dict.keys() # a list of keys
 +dict.values() # a list of values
 +len(dict) # size of dict
 +
 +#example, obj property
 +dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'};
 +
 +#example, obj list
 +dict = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
 +
 +# delete key
 +my_dict.pop(key_name, None)
 +</code>
 +  * set operation <code python>
 +set( ("hello", "world", "of", "words", "of", "world") )
 +# set(['world', 'hello', 'words', 'of'])
 +
 +fib=set( [1,1,2,3,5,8,13] )
 +prime=set( [2,3,5,7,11,13] )
 +combine=fib | prime # set([1, 2, 3, 5, 7, 8, 11, 13])
 +common=fib & prime #set([2, 3, 5, 13])
 +diff = fib - prime # set([8, 1])
 +fib ^ prime # combine - common #set([1, 7, 8, 11])
 +
 +# <, <=, >, >=, ==, !=, in, not in works in set
 +len(fib)=6
 +max(fib)=13
 +min(fib)=1
 +sum(fib)=32
 +any(set([false,true])) # For sets, Return True if there exists any item which is True.
 +all(set([false,true])) # For sets, Return True if there all items which is True.
 +set.add(new)
 +set.remove(old)
 +</code>
 +
 +  * list attribute of a object <code python>object.__dict__</code>
 +===== turn a file into a module =====
 +
 +  * a file can be a module like <code python>
 +import FileName
 +FileName.functionName() # call function side that file
 +</code>
 +
 +
 +===== turn a folder into a module =====
 +
 +  * you can turn a list of python file under same folder into a module by create a empty file under the directory: <code>__init__.py</code>
 +    * it will automatically turn the folder into a module so that you can call each file after import folder like <code python>
 +import FolderName
 +from FolderName import FileName
 +</code>
 +    * whenever you call import FolderName, the __init__py will be run-ed first, so you can put some code inside to do some pre-run task, or append the folder into python path <code python __init__.py>
 +import os, sys
 +my_path = os.path.dirname(__file__)
 +my_path in sys.path or sys.path.append(my_path)
 +</code>
 +
 +===== check module installed =====
 +
 +  * python code <code python>help(moduleName)
 +dir(moduleName)
 +#import moduleName
 +#then type moduleName to show location
 +</code>
 +  * check module in shell command <code bash>pydoc moduleName</code>
 +  * change current working path<code python>os.chdir('/my_path/')
 +</code>
 +
 +===== cross python file reference=====
 +
 +  * import python function from another python file<code python>
 +import sys
 +sys.path.append("/home/my/python_file_folder" # you need to have python path knows your file location
 +# method 1
 +import sample  
 +sample.abcd()
 +# method 2
 +from sample import abcd  
 +abcd()
 +#method 3
 +from sample import *
 +abcd()
 +</code>
 +  * run another python script <code python>
 +execfile("testScript.py")
 +# python 3, that one not work, use this one
 +exec(open("testScript.py").read(), globals())
 +</code>
 +  * pass parameter to a python script (ref: http://www.tutorialspoint.com/python/python_command_line_arguments.htm) <code python>
 +# method 1: python myscript.py arg1 arg2
 +import sys
 +
 +print 'Number of arguments:', len(sys.argv), 'arguments.'
 +print 'Argument List:', str(sys.argv)
 +
 +# method 2
 +import getopt, sys
 +</code>
 +  * example code for a mel file to html file process, in both module and app way
 +    * run code <code bash>python viewMel.py myMelFunc.mel</code>
 +    * run in python <code python>import viewMel
 +viewMel.viewMel_html('myMelFunc.mel','mydoc.html')
 +</code>
 +    * code <code python>
 +#view mel lib
 +#a python tool to view mel lib file functionts
 +import sys
 +
 +def viewMel(file):
 +    with open(file) as f:
 +        for line in f:
 +            lineClean=line.strip()
 +            if lineClean.startswith('//==') or lineClean.startswith('global proc') or lineClean.startswith('/**'):
 +                print lineClean.replace('{','').replace('*/','').replace('/**',' - ')
 +                
 +def viewMel_html(melFile,htmlFile):
 +    h = open(htmlFile,'w')
 +    with open(melFile) as f:
 +        h.write('<html><head><link rel="stylesheet" type="text/css" href="s1.css"></head>\n<body>'+'\n')
 +        for line in f:
 +            lineClean=line.strip()
 +            if lineClean.startswith('//=='):
 +                h.write('<h1>'+lineClean.replace('//==','').replace('=','')+'</h1>'+'\n')
 +            elif lineClean.startswith('global proc'):
 +                 h.write('<h5>'+lineClean.replace('{','').replace('global proc ','')+'</h5>'+'\n')
 +            elif lineClean.startswith('/**'):
 +                 h.write('<p>'+lineClean.replace('*/','').replace('/**',' - ')+'</p>'+'\n')
 +        h.write('</body></html>'+'\n')
 +    h.close()
 +    
 +if __name__=="__main__":
 +    viewMel_html(sys.argv[1],sys.argv[1].replace('.mel','.html'))
 +</code>
 +
 +===== web link operation =====
 +
 +  * open url in brower <code python>import webbrowser
 +webbrowser._browsers # list registered browsers
 +
 +new = 2 # open in a new tab, if possible
 +# open a public URL, in this case, the webbrowser docs
 +url = "http://docs.python.org/library/webbrowser.html"
 +webbrowser.open(url,new=new)
 +# for using firefox, note, firefox needs to be default or in the system environment path
 +webbrowser.get('firefox').open(url,new=2)
 +# open an HTML file on my own (Windows) computer
 +url = "file://X:/MiscDev/language_links.html"
 +webbrowser.open(url,new=new)
 +</code>
 +
 +===== file operation =====
 +  * check file exists <code python>os.path.isfile(path)</code>
 +  * access file and folder<code python>
 +# delete a file
 +os.remove(tmpitem)
 +
 +# check exist of a file
 +if not os.path.exists(tmpJPG):
 +    print tmpitem
 +
 +# get file with a certain name pattern in a directory and all its sub-directories
 +scanDir = r'D:\scanDir_start_dir'
 +my_list = []
 +for (dirpath, dirnames, filenames) in os.walk(scanDir):
 +    my_list.extend( [ os.path.join(dirpath, x) for x in filenames if re.match('[a-zA-Z]+_lang_.+\.json', x) ] )
 +
 +# get file with a certain name pattern in a directory
 +[x for x in os.listdir(scanDir) if os.path.isfile(os.path.join(scanDir, x)) and re.match('MyApp_lang_.+\.json', x) ]
 +[x for x in glob.glob(os.path.join(scanDir,'MyApp_lang_*.json')) if os.path.isfile(x) ]
 +</code>
 +  * lines to array<code python>
 +with open(fname) as f:
 +    content = f.readlines()
 +
 +with open(fname) as f:
 +    for line in f:
 +        #do something with line    
 +</code>
 +  * remove any non-ascii words in file names <code python>
 +import os
 +import urllib
 +curdir = os.getcwd()
 +os.chdir(parentDir)
 +file_list_en = os.listdir('.') # this is ok ascii list
 +file_list_cn = os.listdir(u'.') # this u fix extreme non-ascii names
 +for i in xrange(len(file_list_en)):
 +    itemName = file_list_en[i]
 +    print(itemName)
 +    if itemName.startswith('%'):
 +        # convert %AA cn name to unicode
 +        new_fixName1 = urllib.unquote(itemName).decode('utf8').replace('+',' ')
 +        # then remove cn chars
 +        new_itemName2 = ''.join([c for c in new_fixName1 if ord(c) < 128 ]).replace('?','').replace('+',' ').strip()
 +        if itemName != new_itemName2:
 +            print([itemName,new_itemName2])
 +            os.rename(itemName,new_itemName2)
 +    else:
 +        itemName = file_list_cn[i] # for non ascii names
 +        # just remove cn chars
 +        new_itemName = ''.join([c for c in itemName if ord(c) < 128 ]).replace('?','').replace('+',' ').strip()
 +        if itemName != new_itemName:
 +            print([itemName,new_itemName])
 +            os.rename(itemName,new_itemName)
 +        
 +os.chdir(curdir)
 +</code>
 +  * read line from a huge file by multiple process at the same time, without accident write operation<code python>
 +import linecache
 +linecache.getline('/path_to_file.txt', 2) # get 2nd line of text, the first line is no. 1, unlike 0 in arrary
 +</code>
 +  * process line by line rewrite <code python>
 +import sys
 +import fileinput
 +
 +for i, line in enumerate(fileinput.input('color_test.htm', inplace=1)):
 +  #sys.stdout.write(line.replace('colour','color'))
 +  if line.find('color') > -1:
 +    color=line.split(';',1)[0]
 +    color=color.split('color',1)[1]
 +    sys.stdout.write(line.replace('</span',(color+'</span')))
 +  else:
 +    sys.stdout.write(line)
 +</code>
 +  * make sure directory is created before create file in defined path <code python>
 +my_filePath = '/your_path/intermedia_folders/file.ext'
 +# method 1: these 2 lines only create last leaf folder, won't deal all the inbetween folders
 +if not os.path.isdir(os.path.dirname(my_filePath)):
 +    os.mkdir(os.path.dirname(my_filePath))
 +    
 +# method 2: these 2 lines create all the needed folders forming the path
 +try: 
 +    os.makedirs(os.path.dirname(my_filePath))
 +except OSError:
 +    pass
 +    
 +# method 3: all in one check and create if needed, like method 2 but not need try and except since it will be ok for exist
 +os.makedirs(os.path.dirname(output_path), exist_ok=True)
 +</code>
 +  * write python data<code python>
 +# write raw data and read raw data
 +os.getcwd()
 +
 +import json
 +def writeDataFile(data, file):
 +    with open(file, 'w') as f:
 +        json.dump(data, f)
 +        
 +def readDataFile(file):
 +    with open(file) as f:
 +        data = json.load(f)
 +    return data
 +    
 +# write dictionary data and read dictionary data
 +import csv
 +def writeDictFile(dict, file):
 +    w = csv.writer(open("output.csv", "w"))
 +    for key, val in dict.items():
 +        w.writerow([key, val])
 +
 +def readDictFile(file):    
 +    dict = {}
 +    for key, val in csv.reader(open(file)):
 +        dict[key] = val
 +    return dict
 +</code>
 +  * UTF-8 unicode file write<code python>
 +############### UTF-8 content write #############
 +import codecs
 +txt=u"<table border=1>\n"
 +i=0
 +for each in a:
 +    i=i+1;
 +    txt=txt+u"<tr><td><a href='{link}'>{title}</a></td><td>{code}</td><td>{idx}</td><tr>\n".format(link=each['link'], title=each['title'],code=each['code'], idx=i)
 +
 +txt=txt+u'</table>'
 +
 +with codecs.open('page.htm',mode='w',encoding='utf-8') as f:
 +    f.write(txt)
 +</code>
 +  * copy directory structure only <code python>
 +for dirpath, dirnames, filenames in os.walk(srcPath):
 +    to_mkdir = os.path.join(tgtPath, dirpath[1+len(srcPath):])
 +    if not os.path.isdir(to_mkdir):
 +        os.mkdir(to_mkdir)
 +os.startfile(tgtPath) # show result folder
 +</code>
 +
 +===== thread operation =====
 +
 +  * run a process or python service/app into another thread, so it wont block current interaction <code python>
 +def app(opt):
 +    if opt == 'Noter':
 +        import threading
 +        th = threading.Thread(target = run_noter)
 +        th.start()
 +def run_noter():
 +    import Noter
 +    Noter.main()
 +</code>
 +
 +====== module operation ======
 +
 +  * How to install extra module automatically with pip
 +    * download pip and use bellow method to manually install it, or run this line of command to get or update pip stuffs. <code python>python -m pip install --upgrade pip wheel setuptools</code>
 +    * once done, you can install rest modules with cmd <code bash>python -m pip install package_name</code>
 +  * How to install wheel package <code dos>REM pip app is at PythonAppDir/Scripts
 +REM make sure that wheel is in 32/64bit matching your python 32/64bit version
 +pip install C:/some-dir/some-file.whl
 +</code>
 +  * How to install extra module into seperate directory <code>
 +python.exe -m pip install --target=D:\my_path\plot_lib matplotlib
 +</code>
 +  * How to install extra modules manually
 +    - download the module source,
 +      * if it has setup.py in its directory, run <code python>python setup.py install</code> to ask python to automatically install it into your python site-package directory
 +      * if it has no setup.py in its directory, or just a single folder or a python .py file, then just include its path in your python path under your system variable or run this command in python cmd line before you call <code python>import sys;myPath='/YourPath/ToTheFolderOrFile/';myPath in sys.path or sys.path.append(myPath)</code>
 +      * **Ideally, if you put the Module Folder (not its parent distribution folder) under the Lib/site-package folder, it should work out of box when you type "import Module" in cmd**, of course, if you want to use specific version of the module or having the module in some other location, use the path way is more controllable to use exactly what you need.
 +      * However, if you want your Python module to be at a specific location, then add it to python path environment variable. For windows advanced system setting > environment variable <code>set PYTHONPATH=%PYTHONPATH%;D:\Dev\PyQT</code>
 +    - distribute your python file with module,
 +      * you can ask user to manually install those modules that used in your python program file
 +      * or just put those dependent file or folder next to your python program file  
 +
 +  * check with module info <code python>
 +# check whether a module has that attribute
 +if hasattr(QtGui, 'QTabWidget'):
 +    print("yes")
 +
 +# try get a function from a module and with fallback option
 +resultFunc = getattr(MyClass, "function_A_name", default_function_if_not_found_A)
 +
 +# check imported module name dictionary
 +if 'MyModule' in sys.modules:
 +    print('yes')
 +</code>
 +  * work with module using variable names <code python>
 +# import PySide
 +__import__('PySide')
 +
 +# same as from PySide import QtNetwork as Net
 +qtModeList = ("PySide", "PyQt4", "PySide2", "PyQt5")
 +qtMode = 0
 +Net = getattr( __import__(qtModeList[qtMode], fromlist=['QtNetwork']), 'QtNetwork')
 +</code>
 +  * import module from its parent directory <code python>
 +import os,sys
 +os.path.join(sys.path[0], '..') in sys.path or sys.path.insert(1, os.path.join(sys.path[0], '..'))
 +import my_module_at_current_file_parent_dir
 +</code>
 +===== useful module list =====
 +
 +==== useful HTML modules ====
 +
 +    * [[devwiki:python:html|python with HTML process]]
 +      * <del>urllib2</del> [[https://pypi.python.org/pypi/lxml/|lxml]] and [[https://pypi.python.org/pypi/requests|requests]]
 +      * BeautifulSoup [[http://www.crummy.com/software/BeautifulSoup/|site]] [[http://www.pythonforbeginners.com/python-on-the-web/scraping-websites-with-beautifulsoup/|tut]]
 +      * Mechanize [[http://wwwsearch.sourceforge.net/mechanize/|site]] [[https://www.ibm.com/developerworks/linux/library/l-python-mechanize-beautiful-soup/|tut]]
 +      * Scrapy [[http://scrapy.org/|site]](xpath expressions, link extractors, item loaders)
 +      * pyquery [[https://pythonhosted.org/pyquery/api.html|api]] [[http://davedash.com/tutorial/pyquery/|tut]]
 +      * dokuwiki: [[http://python-dokuwiki.readthedocs.io/en/latest/|dokuwiki interaction]]
 +
 +  * embed code editor info:
 +    * http://eli.thegreenplace.net/2011/04/01/sample-using-qscintilla-with-pyqt
 +    * https://pypi.python.org/pypi/QScintilla#downloads
 +    * http://stackoverflow.com/questions/40002373/qscintilla-based-text-editor-in-pyqt5-with-clickable-functions-and-variables
 +    * http://pyqt.sourceforge.net/Docs/QScintilla2/
 +  * embed python console info:
 +    * code module
 +    * http://epydoc.sourceforge.net/stdlib/code.InteractiveConsole-class.html
 +    * http://code.activestate.com/recipes/355319-using-codeinteractiveconsole-to-embed-a-python-she/
 +    * https://docs.python.org/3/library/code.html
 +    * http://docs.cryengine.com/pages/viewpage.action?pageId=26217213
 +    * http://www.qtcentre.org/threads/29773-Embedding-PyQt4-into-an-Application-running-Python-via-Boost-Python
 +
 +==== useful automation modules ====
 +
 +    * a online book related: https://automatetheboringstuff.com/#toc
 +    * winsys: admin automation for windows
 +      * ref: http://timgolden.me.uk/python/winsys/index.html
 +    * pywinauto: https://github.com/pywinauto/pywinauto/wiki/UI-Automation-tools-ratings
 +      * tutorial:
 +        * DevNami - Python Pyautogui Take Screenshot : https://www.youtube.com/watch?v=a6pHYIXqqSg
 +        * SF Python - https://www.youtube.com/watch?v=dZLyfbSQPXI
 +        * https://automatetheboringstuff.com/
 +      * additional ref: 
 +        * https://www.youtube.com/watch?v=mhNIHgJPP3g
 +        * http://www.programcreek.com/python/index/322/win32gui
 +    * selenium: web browser automation
 +      * the doc: http://selenium-python.readthedocs.io/installation.html
 +
 +==== useful Graphic modules ====
 +
 +    * numpy : numerical python
 +    * matplotlib : plot graphing tool (my note: [[devwiki:python:matplotlib|python with matplotlib]])
 +    * xlrd : excel read tool
 +    * pandas : data structures and data analysis tools (loading datas and data operation like SQL)
 +      * http://pandas.pydata.org/
 +    * PyQtGraph :  plot graphing tool
 +    * Gnuplot : plot tool
 +    * ipython : interactive python and visualization tool
 +    * OpenEXR
 +      * IlmImf, a library that reads and writes OpenEXR images. 
 +      * Half, a C++ class for manipulating half values 
 +      * Imath, a math library with support for matrices, 2d- and 3d-transformations, solvers for linear/quadratic/cubic equations, and more. 
 +      * exrdisplay, a sample application for viewing OpenEXR images at various exposure.
 +    * wxPython : wx GUI
 +    * PyQt/PySide :  Qt GUI
 +    * pysqlite/MySQL_python : sql database
 +    * Graph (node graph): https://www.nodebox.net/code/index.php/Graph
 +    * NetworkX (node graph big): http://networkx.github.io/
 +
 +==== useful audio module ====
 +
 +    * pyaudio <code python>import pyaudio
 +p = pyaudio.PyAudio()
 +for i in range(p.get_device_count()):
 +    print p.get_device_info_by_index(i).get('name')
 +</code>
 +  * useful Game building modules
 +    * quick tutorial on python openGL game: http://www.youtube.com/watch?v=G0jWMoOEFxc
 +
 +==== database and file modules ====
 +
 +    * sqlite (built-in):
 +      * additional desktop sqlite commandline tools: https://www.sqlite.org/download.html
 +    * json (built-in ascii out)
 +    * cPickle (built-in binary out)
 +
 +  * encryption modules
 +    * hashlib (built-in) <code python>
 +m = hashlib.md5()
 +m.update(user_input_text)
 +# a sample hash code example
 +if m.hexdigest() != '88888888888888888888888888888888':
 +    return
 +</code>
 +
 +  * data hashing, like compress data into a string, good for big data compare and record
 +    * hash (built-in, not need import) 64bit, take a object and get a int for quick compare
 +    * hashlib (built-in, need import) 128bit+ and more option like md5, sha for more secure and digital signiture
 +==== system info modules ====
 +
 +    * getpass (built-in, user name) <code python>getpass.getuser()</code>
 +    * glob (built-in, wild char file list) <code python>glob.glob(os.path.join(cur_path,prefix)+'_v???.[Mm][abAB]')
 +# get all file named prefix variable _v001 like ma/mb file</code>
 +    * stat (built-in, file permission) <code python>
 +# lock down file for user, group, other readonly
 +os.chmod(filePath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)</code>
 +    * time (built-in, time related) <code python>time.strftime('%Y/%m/%d %H:%M') # 1980/12/31 23:55 format</code>
 +
 +==== system command modules ====
 +
 +    * subprocess (built-in) 
 +      * subprocess.Popen don't block your main program  <code python>subprocess.Popen([r'notepad.exe', r'D:\zTmp\coder.py']);print('ok')
 +</code>
 +      * subprocess.call will block the main program till call finish, equals <code python>subprocess.call('nircmd.exe win close class "CabinetWClass"')
 +subprocess.call(r'notepad.exe D:\zTmp\coder.py');print('ok')
 +subprocess.Popen([r'notepad.exe', r'D:\zTmp\coder.py']).wait();print('ok')
 +os.system(r'notepad.exe D:\zTmp\coder.py');print('ok')
 +</code>
 +      * subprocess.check_call will raise an exception
 +      * both return result
 +      * Ask System to Open Folder <code python>
 +if os.path.isdir(folderPath):
 +    if sys.platform == 'darwin':
 +        try:
 +            subprocess.check_call(['open', '--', folderPath])
 +        except subprocess.CalledProcessError:
 +            pass # handle errors in the called executable
 +        except OSError:
 +            pass # executable not found
 +    elif sys.platform == 'linux2':
 +        try:
 +            subprocess.check_call(['xdg-open', folderPath])
 +        except subprocess.CalledProcessError:
 +            pass # handle errors in the called executable
 +        except OSError:
 +            pass # executable not found
 +    elif sys.platform in ['win32','win64']:
 +        try:
 +            subprocess.check_call(['explorer', folderPath.replace("/","\\")])
 +        except subprocess.CalledProcessError:
 +            pass # handle errors in the called executable
 +        except OSError:
 +            pass # executable not found
 +</code>
 +      * check if a process is running <code python>
 +appName = 'googledrivesync.exe'
 +info=subprocess.Popen('tasklist.exe /FO CSV /FI "IMAGENAME eq {0}"'.format(appName),stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 +out, err = info.communicate()
 +#out = str(out) # for py3 byte convert
 +out = out.decode('ascii')
 +result = [ x for x in out.replace('"','').split('\r\n') if x !='' ]
 +if len(result)>1 and result[1].startswith(appName):
 +    print('process found: '+appName)
 +else:
 +    subprocess.Popen([r'D:\path_to_app\googledrivesync.exe'])
 +</code>
 +      * ask System to open file <code python>
 +if sys.platform in ['win32','win64']:
 +    os.startfile(filePath)
 +elif sys.platform == 'darwin':
 +    os.open(filePath)
 +elif sys.platform == 'linux2':
 +    os.xdg-open(filePath)
 +</code>
 +      * launch a subprocess to run a Bat file or python file in desktop <code python>
 +subprocess.Popen([r'D:\App\Python27\python.exe',r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.py'])
 +subprocess.Popen([r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.bat'])
 +# force it to open in new console window instead of inside parent console window
 +subprocess.Popen([r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.bat'], creationflags=subprocess.CREATE_NEW_CONSOLE) 
 +
 +# if above method failed because PATH or PYTHONPATH issue, especially when call like above in a mixed python environment like Maya,
 +# you need to use a clean copy of environment, like this Example
 +env2={}
 +env2['PATH']='''C:\Windows\system32
 +D:\App\Python27
 +D:\App\Python27\Lib\site-packages\PyQt4
 +D:\App\Python27\lib\site-packages\pywin32_system32
 +'''.replace('\n',';')
 +env2['PYTHONPATH']='''D:\App\Python27\python27.zip
 +D:\App\Python27\DLLs
 +D:\App\Python27\lib
 +D:\App\Python27\lib\plat-win
 +D:\App\Python27\lib\lib-tk
 +D:\App\Python27
 +D:\App\Python27\lib\site-packages
 +D:\App\Python27\lib\site-packages\win32
 +D:\App\Python27\lib\site-packages\win32\lib
 +D:\App\Python27\lib\site-packages\Pythonwin
 +'''.replace('\n',';')
 +
 +subprocess.Popen([r'D:\App\Python27\python.exe',r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.py'], env=env2)
 +subprocess.Popen([r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.bat'], env=env2)
 +</code>
 +    * subprocess a console app and get return, and optionally hide the pop up console when call <code python>
 +# hide popup console for win platform only
 +startupinfo = subprocess.STARTUPINFO()
 +startupinfo.dwFlags |= 1 #subprocess.STARTF_USESHOWWINDOW
 +# startupinfo.wShowWindow = subprocess.SW_HIDE #(optional)
 +
 +info=subprocess.Popen( '{0}'.format(app_path),stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE,  startupinfo=startupinfo)
 +out, err = info.communicate()
 +out = out.decode('ascii') #str(out) # for py3 byte convert
 +</code>
 +==== string related ====
 +
 +    * re (built-in, regular expression) <code python>
 +# capture group and non-capture group:
 +# http://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group-what-does-a-question-mark-followed-by-a-colon
 +re.findall('([a-zA-Z0-9_]+)_atn(?:Msg)?', 'test_atn') # return ['test']
 +re.findall('([a-zA-Z0-9_]+)_atn(?:Msg)?', 'test_atnMsg') # return ['test']
 +# since we only need the prefix, the Msg group is to test exisits
 +</code>
 +
 +==== compile and de-compile module ====
 +
 +**cx_Freeze**: compile to exe, app, bin
 +  * After compile, the only files need to do partial update each time is (exe + library.zip)
 +
 +**PyInstaller**: compile py into binary, deeper than cx_Freeze, 
 +
 +<code>
 +# cmd install
 +python -m pip install pyinstaller
 +
 +# compile, cd to your py file first, you may need copy your supporting folder to dist path after compile
 +python.exe -m PyInstaller MyApp.py
 +
 +# no terminal version
 +python.exe -m PyInstaller MyApp.py --noconsole
 +# set app binary icon
 +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.ico
 +
 +# exclude tk and tcl library
 +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.ico --exclude-module=FixTk --exclude-module=tk --exclude-module=tcl --exclude-module=_tkinter --exclude-module=tkinter --exclude-module=Tkinter
 +
 +# for mac, if failed to build on pyconfig.h
 +# - fix pyconfig.h; by cmd: sudo touch /Library/Python/3.8/include/pyconfig.h
 +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.icns
 +
 +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.icns -w --onefile
 +
 +# cmd+i to check app info, and drag icns file to app/folder icon logo to set the icon
 +# manually turn folder into app, just add .app behind folder name, folder name must be same as main binary file
 +
 +# when run result mac app on other computer, if security stop it run like "app is damaged", run this command on created app or app folder
 +xattr -cr "/Applications/MyAppFileorMyAppFolder.app"
 +</code>
 +
 +**uncompyle2**: convert pyc to py (python 2.7 code support only) 
 +
 +https://github.com/wibiti/uncompyle2
 +
 +  * code example <code python># in the init file
 +main('/pyc_file_dir/', '/pyc_file_dir_decompile_dir/', ['that_pyc_file.pyc'], [])
 +</code>
 +
 +====== Python Important Modules ======
 +===== pip =====
 +
 +  * pip is a quick python module get and installer module for python, it can download module for your python library.
 +  * check version <code>python -m pip --version</code>
 +  * update it before install other module, make sure you already in python' pip folder path <code>sudo pip install --upgrade pip
 +# or
 +python -m pip install --upgrade pip 
 +</code>
 +===== re =====
 +
 +  * regular expression pattern
 +
 +^ all data file | <code>(?!(.*[.]db)|(^Keyboard$)|(^[.].*))</code>|
 +^ all maya file | <code>[^.].+(.ma|.mb|.abc|.xml|.fbx)</code>|
 +
 +  * variable name case conversion
 +    * CamelCase to "snake_case" or "sentence case"
 +    * code: <code python>
 +def convert(name, gap='_'):
 +    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1'+gap+r'\2', name) # separate more generic AAAABc case to AAAA Bc
 +    return re.sub('([a-z0-9])([A-Z])', r'\1'+gap+r'\2', s1) # main conversion
 +#get2HTTPResponseCode > get2_HTTP_Response_Code
 +
 +def convert2(name, gap='_'):
 +    return re.sub('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))', gap+r'\1', name) # main conversion
 +</code>
 +===== requests =====
 +
 +  * 3rd party module, html get post request response operation library
 +  * header list: https://deviceatlas.com/blog/list-of-user-agent-strings
 +
 +  * **Single Get web link content** <code python>
 +import requests
 +result = requests.get('http://my_site/my_page', timeout=5)
 +print(result.text)
 +</code>
 +  * **Single post web link content with login info** <code python>
 +result = requests.post('http://my_site/my_page', data={'site_username_field_name': 'your_name', 'site_password_field_name': 'your_pass'})
 +print(result.text)
 +</code>
 +  * **one-time login with continue consistent header info for session operation** <code python>
 +with requests.Session() as session:
 +    # first time login,
 +    reply = session.post('http://site/login_page', data={'site_username_field_name': 'your_name', 'site_password_field_name': 'your_pass'})
 +    print(reply.text)
 +    # read rest login-required pages
 +    reply = session.get('http://site/other_info_page')
 +    print(reply.text)
 +</code>
 +  * check send header and received header info <code python>
 +print(session_object.headers) # user sending header info
 +print(reply_object.headers) # server replying header info
 +</code>
 +  * more function explain <code python>
 +result.content # is html in bytes
 +result.text # is html in unicode text
 +</code>
 +  * disable non secure cert warning <code python>requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)</code>
 +===== PIL and Pillow=====
 +
 +  * PIL, Python Imaging Library (Fork) =  Pillow, install the lib <code>python -m pip install Pillow</code>
 +  * python code <code python>
 +from PIL import Image, ImageDraw, ImageOps
 +</code>
 +
 +===== BeautifulSoup =====
 +
 +  * a html info extractor, best works with another parser module "lxml" to get partial info
 +  * ref: https://beautiful-soup-4.readthedocs.io/en/latest/#installing-a-parser
 +    * parser is a html interprater, as not all html is clean format and tidy, some parser can auto fix some html tag miss match.
 +  * example code <code python>
 +from bs4 import BeautifulSoup
 +soup = BeautifulSoup(html, 'lxml') # html byte from requests.get
 +# find all title link text
 +link_list = soup.find_all("a", {'class':['title', 'threadtitle_unread']} ) # or 'title' only if no multiple
 +name_list = [x.string for x in link_list ] # text element extract
 +link_list[0].attrs['href'] # link url element extract
 +</code>
 +
 +
 +===== selenium =====
 +
 +  * you need install 2 things to get it working:
 +    - Python module: 
 +      - for py2.7: https://pypi.python.org/pypi/selenium/2.7.0
 +      - for py3.x: https://pypi.python.org/pypi/selenium
 +    - system platform dependent webdriver binary: http://www.seleniumhq.org/download/
 +      - for chrome browser automate: go above link and find 3rd binding "Google Chrome Driver"
 +    - then you can load that drive binary into python selenium module object to start <code python>
 +from selenium import webdriver
 +driver = webdriver.Chrome('/your_path_to_that_binary_driver/chromedriver.exe')
 +driver.get('http://google.com')
 +driver.implicitly_wait(20)
 +driver.get_screenshot_as_file(r'D:\testshot.png')
 +driver.quit()
 +</code>
 +  * get element <code python>
 +# get css selector by right click browser's inspector view, right click on html element to copy css selector
 +driver.find_element_by_css_selector('.jsb > center:nth-child(1) > input:nth-child(1)'
 +# path method
 +driver.find_element_by_xpath('//input[@name="btnk"]')
 +</code>
 +  * element operation
 +    * element.get_attribute('href')
 +    * element.get_attribute('outerHTML')
 +    * element.text
 +    * element.click()
 +    * element_input.send_keys() and webdriver.common.keys
 +    * element_form.submit()
 +    * driven_browser.back(), forward(), refresh()
 +
 +  * additional tool:
 +    * selenium ide firefox - a all-in-one test tool for it: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/
 +      * tutorial: http://www.seleniumeasy.com/selenium-tutorials/css-selectors-tutorial-for-selenium-with-examples
 +===== PyAutoGUI =====
 +
 +  * GUI cross-platform GUI interaction automation 
 +    * ref: https://www.youtube.com/watch?v=dZLyfbSQPXI&t=392s
 +    * tutorial: https://www.youtube.com/results?search_query=PyAutoGUI
 +  * full list of library required in download page: https://pypi.python.org/pypi/PyAutoGUI
 +
 +  * mouse control
 +    * click(), doubleClick(), rightClick()
 +    * click([x,y])
 +    * moveTo(x,y [,duration=sec])
 +    * moveRel(x_offset, y_offset [,duration=sec])
 +    * dragTo(x,y [,duration=sec])
 +    * position(), size(), 
 +    * displayMousePosition() <code python>
 +# note, that displayMousePosition require 3rd party library
 +# * PIL (Py2.x only, adapted by Pillow) > PIL.ImageGrab > pyscreenze (edit line 309 with "from PIL import ImageGrab") > pyautogui
 +http://www.pythonware.com/products/pil/
 +https://pypi.python.org/pypi/PyScreeze
 +</code>
 +
 +  * keyboard control
 +    * typewrite('Text goes here', [,interval=secs]) # pyautogui.KEYBOARD_KEYS
 +    * press('pageup')
 +    * hotkey('ctrl', 'o')
 +    * note: best work with time.sleep(5) to give 5 sec for pc to respond
 +
 +  * image recognition
 +    * pixel(x,y) # return RGB
 +    * screenshot([filename]) # return PIL,Pillow Image object [and save file]
 +    * locateOnScreen(imageFilename) # returns (x,y, w, h) or None
 +    * linux: scrot
 +    * example code <code python>
 +import pyautogui as pui
 +pos = pui.locateOnScreen('d:\\zTmp\\test_build.png')
 +pui.click( (pos[0]+pos[2]*0.5 , pos[1]+pos[3]*0.5) )
 +</code>
 +
 +===== SikuliX =====
 +
 +  * SikuliX automates anything you see on the screen of your desktop computer running Windows, Mac or some Linux/Unix. Link: http://sikulix.com
 +  * tutorial: https://www.youtube.com/watch?v=VdCOg1bCmGo
 +
 +===== pyautoit =====
 +
 +  * a python binding for AutoIt tool, a windows only automation software
 +    * https://github.com/jacexh/pyautoit
 +    * AutoIt home page: https://www.autoitscript.com/site/autoit/
 +    * API level interaction
 +
 +
 +===== pywinauto =====
 +
 +  * a mostly windows only GUI automation module, with mouse, keyboard support on linux
 +    * API level interaction
 +
 +===== pywin32 - win32com =====
 +
 +  * tip and ref: 
 +    * http://timgolden.me.uk/python/index.html
 +    * http://timgolden.me.uk/pywin32-docs/html/com/win32com/HTML/QuickStartClientCom.html
 +  * COMbrowser: win32com\client\combrowse.py
 +
 +  * install and restart computer to get dll reg refreshed <code>
 +https://sourceforge.net/projects/pywin32/files/pywin32/
 +(or) python -m pip install pypiwin32</code>
 +
 +  * get windows explorer in taskbar order, return [(title, hwnd int, addressbar url, [top,left,w,h], vis)] <code python>
 +import win32com
 +from win32com import client
 +def get_explorer_list():
 +    # win32com: get folder windows in taskbar order; return [(title, hwnd int, addressbar url, [top,left,w,h], vis)]
 +    shell = client.Dispatch("Shell.Application")
 +    win_list = shell.Windows()
 +    result_list = []
 +    for win in win_list:
 +        info = []
 +        info.append(win.LocationName) # title name
 +        info.append(win.hwnd) # handle value as int
 +        info.append(win.LocationURL) # address bar url, eg. file:///C:/Users/Test/Downloads
 +        info.append([win.Top, win.Left, win.width, win.height]) # win pixel position
 +        info.append(win.visible) # visible
 +        info.append(win) # com object
 +        result_list.append(info)
 +    return result_list
 +''' full list of property
 +['ReadyState', 'Busy', 'Container', 'Silent', 'Top', 
 +'RegisterAsDropTarget', 'LocationName', 'Application', 'Offline', 'Document', 
 +'Type', 'ToolBar', 'MenuBar', 'FullScreen', 'Parent', 'TheaterMode', 
 +'Path', 'Name', 'RegisterAsBrowser', 'StatusText', 'Left', 'TopLevelContainer', 
 +'Resizable', 'Width', 'StatusBar', 'HWND', 'Height', 'Visible', 'FullName', 
 +'LocationURL', 'AddressBar']
 +'''
 +</code>
 +  * get selected item in window explorer <code python>
 +# ref: 
 +# https://win32com.goermezer.de/microsoft/windows/find-selected-files-in-windows-explorer.html
 +# https://msdn.microsoft.com/en-us/library/windows/desktop/bb787810(v=vs.85).aspx
 +
 +def get_selection_list(win):
 +    # win32com: get selection item detail from win COM object
 +    item_list = []
 +    folderItems = win.Document.SelectedItems()
 +    for i in range(folderItems.Count):
 +        tmp_info = []
 +        tmp_info.append(folderItems.Item(i).path) # C:\Users\Test\Downloads\example.zip
 +        tmp_info.append(folderItems.Item(i).name)
 +        tmp_info.append(folderItems.Item(i).isFolder)
 +        tmp_info.append(folderItems.Item(i).size)
 +        tmp_info.append(folderItems.Item(i).type)
 +        item_list.append(tmp_info)
 +    return item_list
 +</code>
 +  * explorer window COM object control <code python>
 +# control the window COM object to go to a location
 +win.Navigate('file:///C:/Users/Test/Desktop')
 +win.Busy # a check for still in processing
 +
 +# check visibile and make full screen
 +win.FullScreen =1 # make first windows explorer full screen
 +</code>
 +  * system operation<code python>
 +# get system dir and 
 +oShell = win32com.client.Dispatch("Wscript.Shell")
 +print(oShell.SpecialFolders("Desktop"))
 +
 +# change drive letter
 +# ref: http://timgolden.me.uk/python/win32_how_do_i/change-a-drive-letter.html
 +
 +# create windows shortcut and read it
 +shortcut = shell.CreateShortCut("t:\\test.lnk")
 +shortcut.Targetpath = "D:\\tmp"
 +shortcut.save()
 +
 +shortcut = shell.CreateShortCut("t:\\test.lnk")
 +print(shortcut.Targetpath)
 +
 +# get single screen resolution and total screen
 +# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724385(v=vs.85).aspx
 +import win32api
 +w=win32api.GetSystemMetrics(0) # primary screen width
 +h=win32api.GetSystemMetrics(1) # primary screen height
 +vw = win32api.GetSystemMetrics(78) # total screen area width
 +vh = win32api.GetSystemMetrics(79) # total screen area height
 +print('screen resolution: {0}x{1}'.format(w,h))
 +print('virtual area resolution: {0}x{1}'.format(vw,vh))
 +</code>
 +===== ctypes =====
 +
 +  * ctypes is an advanced ffi (Foreign Function Interface) package, ctypes allows to call functions in dlls/shared libraries and wrap libraries in pure Python. ctypes works cross-platform with libffi support.
 +    * ref: http://nullege.com/codes/search/ctypes
 +    * my doc on windows API and DLL functions [[devwiki:winapi|WinAPI]]
 +  * (method 1) get all windows explorer windows in Z order, return [(title, class, [x,y,w,h], hwnd int)] <code python>
 +def get_win_order_all():
 +    # ctype: grab folder windows in z order, return [(title, class, [x,y,w,h], hwnd int)]
 +    from ctypes import wintypes
 +    order_list = []
 +    result_list = []
 +    top = ctypes.windll.user32.GetTopWindow(None)
 +    if not top:
 +        return order_list
 +    length = ctypes.windll.user32.GetWindowTextLengthW(top)
 +    buff = ctypes.create_unicode_buffer(length + 1)
 +    ctypes.windll.user32.GetWindowTextW(top, buff, length + 1)
 +    class_name = ctypes.create_string_buffer(200)
 +    ctypes.windll.user32.GetClassNameA(top, ctypes.byref(class_name), 200)
 +    r=ctypes.wintypes.RECT()
 +    ctypes.windll.user32.GetWindowRect(top,ctypes.byref(r))
 +    # class_name.value in py3 is byte, while py2 is string
 +    if isinstance(class_name.value, str):
 +        result_list.append( [buff.value, class_name.value, [r.left,r.top,r.right-r.left,r.bottom-r.top], top ])
 +    else:
 +        result_list.append( [buff.value, class_name.value.decode('ascii'), [r.left,r.top,r.right-r.left,r.bottom-r.top], top ])
 +    order_list.append(top)
 +    while True:
 +        next = ctypes.windll.user32.GetWindow(order_list[-1], 2) # win32con.GW_HWNDNEXT
 +        if not next:
 +            break
 +        length = ctypes.windll.user32.GetWindowTextLengthW(next)
 +        buff = ctypes.create_unicode_buffer(length + 1)
 +        ctypes.windll.user32.GetWindowTextW(next, buff, length + 1)
 +        class_name = ctypes.create_string_buffer(200)
 +        ctypes.windll.user32.GetClassNameA(next, ctypes.byref(class_name), 200)
 +        r=ctypes.wintypes.RECT()
 +        ctypes.windll.user32.GetWindowRect(next,ctypes.byref(r))
 +        # class_name.value in py3 is byte, while py2 is string
 +        if isinstance(class_name.value, str):
 +            result_list.append( [buff.value, class_name.value, [r.left,r.top,r.right-r.left,r.bottom-r.top], next] )
 +        else:
 +            result_list.append( [buff.value, class_name.value.decode('ascii'), [r.left,r.top,r.right-r.left,r.bottom-r.top], next] )
 +        order_list.append(next)
 +    return result_list
 +
 +full_list = get_win_order_all()
 +visible_list = [x for x in full_list if ctypes.windll.user32.IsWindowVisible(x[-1])]
 +# visible list is actually more useful, 10+ instead 100+ items
 +
 +explorer_list = [x for x in visible if x[1]=='CabinetWClass']
 +firefox_list = [x for x in visible if x[1]=='MozillaWindowClass']
 +</code>
 +  * (method 2) get all (visible) (windows explorer) windows in Z order, return [(title, class, hwnd as pointer)]<code python>
 +def get_win_order_v2():
 +    # ctypes
 +    # ref: https://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/
 +    # info (title, class_name, hwnd)
 +    window_list = []
 +    # propare windows list passing
 +    def window_info_process(hwnd, lParam):
 +        if ctypes.windll.user32.IsWindowVisible(hwnd):
 +            length = ctypes.windll.user32.GetWindowTextLengthW(hwnd)
 +            buff = ctypes.create_unicode_buffer(length + 1)
 +            ctypes.windll.user32.GetWindowTextW(hwnd, buff, length + 1)
 +            class_name = ctypes.create_string_buffer(200)
 +            ctypes.windll.user32.GetClassNameA(hwnd, ctypes.byref(class_name), 200)
 +            t_class_name = class_name.value
 +            if not isinstance(t_class_name, str):
 +                t_class_name = class_name.value.decode('ascii') # py3 fix
 +            if t_class_name == 'CabinetWClass':
 +                window_list.append( [ buff.value, t_class_name, hwnd ])
 +        return True
 +    procObj = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
 +    # execute it
 +    ctypes.windll.user32.EnumWindows(procObj(window_info_process), 0)
 +    return window_list
 +</code>
 +  * (method 3) Break down ctypes hwnd, pid, process_handle into functions modules <code python>
 +'''
 +ref: http://timgolden.me.uk/python/win32_how_do_i/find-the-window-for-my-subprocess.html
 +    Window handle → process ID: GetWindowThreadProcessId
 +    Process ID → process handle: OpenProcess
 +    Process handle → executable path: QueryFullProcessImageName (compare this with your target)
 +    Close the process handle: CloseHandle
 +ref: http://www.mengwuji.net/forum.php?mod=viewthread&tid=1432
 +    Windows 2000 = GetModuleFileName()
 +    Windows XP x32 = GetProcessImageFileName()
 +    Windows XP x64 = GetProcessImageFileName()
 +    Windows Vista = QueryFullProcessImageName()
 +    Windows 7 = QueryFullProcessImageName()
 +ref:
 +    uisoup - utils : https://pypi.python.org/pypi/UISoup
 +    https://programtalk.com/python-examples/ctypes.windll.user32.GetWindowThreadProcessId/
 +    https://pypkg.com/pypi/pywinauto/f/pywinauto/win32functions.py
 +'''
 +
 +def get_win_pid(hwnd):
 +    cur_pid = ctypes.c_long()
 +    ctypes.windll.user32.GetWindowThreadProcessId(hwnd, ctypes.byref(cur_pid))
 +    return cur_pid
 +def get_process_path(pid):
 +    PROCESS_QUERY_INFORMATION = 0x0400
 +    max_unicode_path = 32767 * 2 + 200
 +    file_path_buffer = ctypes.create_unicode_buffer(max_unicode_path)
 +    length_path = ctypes.c_ulong(max_unicode_path)
 +    pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) # won't get admin process if not as admin
 +    retval = ctypes.windll.kernel32.QueryFullProcessImageNameW(pHandle, 0, file_path_buffer, ctypes.byref(length_path))
 +    if retval == 0:
 +        raise Exception('QueryFullProcessImageNameW failed')
 +    #ctypes.windll.kernel32.CloseHandle(pHandle)
 +    return file_path_buffer.value
 +def get_win_path(hwnd):
 +    return get_process_path(get_win_pid(hwnd))
 +
 +def get_win_rect(hwnd):
 +    from ctypes import wintypes
 +    r=ctypes.wintypes.RECT()
 +    ctypes.windll.user32.GetWindowRect(hwnd,ctypes.byref(r))
 +    return [r.left,r.top,r.right-r.left,r.bottom-r.top]
 +def get_win_title(hwnd):
 +    length = ctypes.windll.user32.GetWindowTextLengthW(hwnd)
 +    title_buffer = ctypes.create_unicode_buffer(length + 1)
 +    ctypes.windll.user32.GetWindowTextW(hwnd, title_buffer, length + 1)
 +    return title_buffer.value
 +# AE CS6: AE_CApplication_11.0
 +def get_win_class(hwnd):
 +    class_name = ctypes.create_string_buffer(200)
 +    ctypes.windll.user32.GetClassNameA(hwnd, ctypes.byref(class_name), 200)
 +    t_class_name = class_name.value
 +    if not isinstance(t_class_name, str):
 +        t_class_name = class_name.value.decode('ascii') # py3 fix
 +    return t_class_name
 +def is_win_top(hwnd):
 +    top_hwnd = ctypes.windll.user32.GetTopWindow(None)
 +    return hwnd == top_hwnd
 +
 +def get_win_list(visible=1, orderMethod=0):
 +    # both method get result
 +    # orderMethod uses : ctypes.windll.user32.GetTopWindow
 +    # callBackMethod uses: ctypes.windll.user32.EnumWindows
 +    win_list = []
 +    if orderMethod:
 +        order_list = []
 +        top = ctypes.windll.user32.GetTopWindow(None)
 +        if not top:
 +            return win_list
 +        visible_info = ctypes.windll.user32.IsWindowVisible(top)
 +        win_list.append( [ top, visible_info ])
 +        order_list.append(top)
 +        while True:
 +            next = ctypes.windll.user32.GetWindow(order_list[-1], 2) # win32con.GW_HWNDNEXT
 +            if not next:
 +                break
 +            visible_info = ctypes.windll.user32.IsWindowVisible(next)
 +            win_list.append( [next, visible_info] ) # hwnd: as int
 +            order_list.append(next)
 +    else:
 +        # propare windows list passing
 +        def window_info_process(hwnd, lParam):
 +            visible_info = ctypes.windll.user32.IsWindowVisible(hwnd)
 +            win_list.append( [ hwnd, visible_info ]) #hwnd: as LP_c_long
 +            return True
 +        # ref: http://makble.com/the-story-of-lpclong
 +        # function 1: this will get as LP_c_long as hwnd
 +        #procObj = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
 +        # function 2: this will get int as hwnd like other win32api function
 +        procObj = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
 +        # execute it
 +        ctypes.windll.user32.EnumWindows(procObj(window_info_process), 0)
 +    if visible:
 +        return [x for x in win_list if x[1]]
 +    return win_list
 +</code>
 +  * ctypes window operation by hwnd<code python>
 +# my_hwnd can be a int or a ctypes int pointer
 +# full function ref: http://www.jasinskionline.com/windowsapi/ref/funca.html
 +
 +# bring window to bring
 +ctypes.windll.user32.SetForegroundWindow(my_hwnd)
 +
 +# close window
 +WM_CLOSE = 0x0010
 +ctypes.windll.user32.PostMessageA(my_hwnd, WM_CLOSE, 0, 0)
 +
 +# get window x,y,w,h
 +from ctypes import wintypes
 +r=ctypes.wintypes.RECT()
 +ctypes.windll.user32.GetWindowRect(my_hwnd,ctypes.byref(r))
 +print( [r.left,r.top,r.right-r.left,r.bottom-r.top] )
 +
 +# move window by x,y,w,h and refresh
 +ctypes.windll.user32.MoveWindow(my_hwnd, 0, 0, 760, 500, True)
 +</code>
 +  * mouse, keyboard operation <code python>
 +# get mouse position
 +class POINT(ctypes.Structure):
 +    _fields_ = [("x", ctypes.c_ulong), ("y", ctypes.c_ulong)]
 +pt = POINT()
 +ctypes.windll.user32.GetCursorPos(ctypes.byref(pt))
 +print('{0} {1}'.format(pt.x,pt.y))
 +
 +ctypes.windll.user32.SetCursorPos(1200, 850)  # move cursor to 1200,850 (x,y)
 +ctypes.windll.user32.mouse_event(2, 0, 0, 0,0) # left mouse button down
 +ctypes.windll.user32.mouse_event(4, 0, 0, 0,0) # left mouse button up
 +</code>
 +  * get and set wallpaper in windows <code python>
 +def getWallpaper():
 +    SPI_GETDESKWALLPAPER = 0x0000073
 +    #    try:
 +    #        win32con.SPI_GETDESKWALLPAPER 
 +    #    except AttributeError, attributeError:
 +    #        raise WinDesktopError, "getWallpaper() not supported on this OS"
 +    wallpaper = ctypes.c_buffer("\000" * 256)
 +    ctypes.windll.user32.SystemParametersInfoA(SPI_GETDESKWALLPAPER, len(wallpaper), ctypes.byref(wallpaper), 0)
 +    return wallpaper.value
 +
 +def setWallpaper(wallpaperPath, conversionPath=None):    
 +    # Convert to bitmap if necessary
 +    wallpaperImage = Image.open(wallpaperPath)
 +    if wallpaperImage.format != 'BMP':
 +        conversionPath = conversionPath or os.getenv('SystemRoot')
 +        wallpaperPath = os.path.join(conversionPath, os.path.splitext(os.path.split(wallpaperPath)[-1])[0] + '.bmp')
 +        wallpaperImage.save(wallpaperPath)
 +    # Set wallpaper
 +    wallpaperPath = ctypes.c_buffer(wallpaperPath)
 +    ctypes.windll.user32.SystemParametersInfoA(win32con.SPI_SETDESKWALLPAPER, 0, wallpaperPath, 0)
 +</code>
 +  * wallpaper set (win7) the simple version <code python>
 +import ctypes
 +SPI_SETDESKWALLPAPER=20
 +ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, r"D:\Rec\bg2.jpg",0)
 +</code>
 +  * set id <code python>ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('com.app.1.0')</code>
 +  * get if user is admin <code python>print(ctypes.windll.shell32.IsUserAnAdmin())</code>
 +  * system operation <code python>
 +# get screen count
 +screen_cnt = ctypes.windll.user32.GetSystemMetrics(80)
 +# get screen resolution
 +screen_x, screen_y = ( ctypes.windll.user32.GetSystemMetrics(78), ctypes.windll.user32.GetSystemMetrics(79) )
 +</code>
 +
 +**file and folder operation**
 +  * show drive letter in use <code python>
 +drive_list_data = ctypes.cdll.msvcrt._getdrives() # binary mask in decimal format
 +mask_list = list( '{0:b}'.format(drive_list) )[::-1]
 +A_code = 65 # ascii code of letter A
 +drive_letter_in_use = [ chr(A_code+i) for i in range(len(mask_list)) if mask_list[i]=='1' ]
 +</code>
 +  * show file and select file in explorer (return 42)<code python>
 +my_filePath = u'C:\\Users\\Public\\Pictures\\Sample Pictures\\Koala.jpg'
 +ctypes.windll.shell32.ShellExecuteW(None, u'open', u'explorer.exe', u'/n,/select,'+my_filePath, None, 1)</code>
 +  * open file property window <code python>
 +import time
 +import ctypes
 +import ctypes.wintypes
 +
 +SEE_MASK_NOCLOSEPROCESS = 0x00000040
 +SEE_MASK_INVOKEIDLIST = 0x0000000C
 +
 +class SHELLEXECUTEINFO(ctypes.Structure):
 +    _fields_ = (
 +        ("cbSize",ctypes.wintypes.DWORD),
 +        ("fMask",ctypes.c_ulong),
 +        ("hwnd",ctypes.wintypes.HANDLE),
 +        ("lpVerb",ctypes.c_char_p),
 +        ("lpFile",ctypes.c_char_p),
 +        ("lpParameters",ctypes.c_char_p),
 +        ("lpDirectory",ctypes.c_char_p),
 +        ("nShow",ctypes.c_int),
 +        ("hInstApp",ctypes.wintypes.HINSTANCE),
 +        ("lpIDList",ctypes.c_void_p),
 +        ("lpClass",ctypes.c_char_p),
 +        ("hKeyClass",ctypes.wintypes.HKEY),
 +        ("dwHotKey",ctypes.wintypes.DWORD),
 +        ("hIconOrMonitor",ctypes.wintypes.HANDLE),
 +        ("hProcess",ctypes.wintypes.HANDLE),
 +    )
 +
 +ShellExecuteEx = ctypes.windll.shell32.ShellExecuteEx
 +ShellExecuteEx.restype = ctypes.wintypes.BOOL
 +
 +sei = SHELLEXECUTEINFO()
 +sei.cbSize = ctypes.sizeof(sei)
 +sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_INVOKEIDLIST
 +sei.lpVerb = "properties"
 +sei.lpFile = 'C:\\Users\\Public\\Pictures\\Sample Pictures\\Koala.jpg'
 +sei.nShow = 1
 +ShellExecuteEx(ctypes.byref(sei))
 +time.sleep(5)
 +</code>
 +
 +  * get clipboard data (ref: https://stackoverflow.com/questions/101128/how-do-i-read-text-from-the-windows-clipboard-from-python) <code python>
 +import ctypes
 +def get_clipboard_text():
 +    ctypes.windll.kernel32.GlobalLock.argtypes = [ctypes.c_void_p]
 +    ctypes.windll.kernel32.GlobalLock.restype = ctypes.c_void_p
 +    ctypes.windll.kernel32.GlobalUnlock.argtypes = [ctypes.c_void_p]
 +    ctypes.windll.user32.GetClipboardData.restype = ctypes.c_void_p
 +
 +    ctypes.windll.user32.OpenClipboard(0)
 +    try:
 +        if ctypes.windll.user32.IsClipboardFormatAvailable(1):
 +            data = ctypes.windll.user32.GetClipboardData(1)
 +            data_locked = ctypes.windll.kernel32.GlobalLock(data)
 +            text = ctypes.c_char_p(data_locked)
 +            value = text.value
 +            ctypes.windll.kernel32.GlobalUnlock(data_locked)
 +            return value
 +    finally:
 +        ctypes.windll.user32.CloseClipboard()
 +# test
 +print(get_clipboard_text())
 +</code>
 +===== twisted =====
 +
 +  * network communication:
 +    * old version with python 2.7 32bit wheel: https://pypi.python.org/pypi/Twisted/15.0.0
 +
 +===== django =====
 +
 +  * a python web server, install by <code>python -m pip install django</code>
 +
 +===== openpyxl=====
 +
 +  * a new excel reader, xlsx format, not old xls format, since now everyone use new excel version
 +  * read excel as dictionary (1st key, rest data) <code python>
 +    def excel2Dict(filename):
 +        # import openpyxl
 +        # max_row = sheet.max_row
 +        # max_column = sheet.max_column
 +        wb = openpyxl.load_workbook(filename=filename, read_only=True)
 +        records = []
 +        for sheet in wb.worksheets:
 +            count = 0
 +            keys = []
 +            
 +            for row in sheet.rows:
 +                temp = []
 +                for cell in row:
 +                    if count == 0:
 +                        keys.append(str(cell.value))
 +                    else:
 +                        temp.append(str(cell.value))
 +                if count > 0:
 +                    records.append( dict(zip(keys, temp)) ) # 
 +                else:
 +                    count += 1
 +        return records
 +</code>
 +  * read excel as row data wo empty cell. <code python>
 +    def excel2Data(filename):
 +        # import openpyxl
 +        wb = openpyxl.load_workbook(filename=filename, read_only=True, data_only=True)
 +        sheet_data = []
 +        for sheet in wb.worksheets:
 +            table_data = []
 +            for row in sheet.values:
 +                row_data = []
 +                for cell in row:
 +                    if cell is not None:
 +                        row_data.append(cell)
 +                if len(row_data)>0:
 +                    table_data.append(row_data)
 +            sheet_data.append(table_data)
 +        return sheet_data
 +</code>
 +====== Python by Reference or by Value ======
 +
 +  * my experience found: "pure = assignment will not change original holder's value, but other operation will change original holder's value"
 +  * more study on Pointer vs Reference: http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/
 +    * Python variable are all reference, so pass reference to a function, the inside function holds copy of the reference, nothing outside affected, but if inside function reference modify, outside will be affected, since they all store value at same memory location(reference to that location): http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference
 +      * http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference
 +    * and another clear explanation of python reference vs c++ reference from c++ user by code: https://hbfs.wordpress.com/2011/06/14/python-references-vs-c-and-c/
 +  * here is my study experiment <code python>
 +
 +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +# Example 1
 +dict = {}
 +dict['seq']={}
 +for each in 'aa bb cc dd'.split():
 +    dict['seq'][each]=[]
 +list_aa = dict['seq']['aa'] # passing by reference!!!
 +for i in [1,2,3,4,5]:
 +    list_aa.append(i)
 +print dict # affect both list_aa and dict
 +    
 +temp_list = dict['seq']['aa']
 +temp_list.append(22) # you are not get a copy of dict entry, but affect orginal dict as well
 +print temp_list
 +print dict # changed ------- because it is a operation
 +
 +# ref: http://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list-in-python
 +temp_list2 = dict['seq']['aa'][:] # actually duplicate the value out, the fastest way
 +temp_list2.append(55)
 +print temp_list2
 +print dict # stay same
 +
 +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +# Example 2
 +locs = [ [1], [2] ]
 +for loc in locs: # passing by values !!!
 +    loc = []
 +print locs # stay same
 +
 +for loc in locs:
 +    loc[0] = loc[0] * 2
 +print locs # changed ------- because it is a operation
 +
 +#~~~~~~~~~~~~~~~~~~~~~~~~
 +# Example 3
 +a=1
 +b=a
 +b=2
 +print a # stay the same
 +
 +a=[1]
 +b=a
 +b=[2]
 +print a # stay the same
 +
 +a=[1]
 +b=a
 +b.append(2)
 +print a # changed ------- because it is a operation
 +
 +# thus: assignment will not change original one, but operation will change original one
 +
 +a=[1,2,3]
 +b=a
 +c=[4,5,6]
 +b=c
 +print a # stay the same
 +
 +a=[1,2, [3.1,3.2,3.3] ]
 +b=a[2]
 +c=[4,5,6]
 +b=c
 +print a # stay the same
 +
 +a=[1,2, [3.1,3.2,3.3] ]
 +b=a[2]
 +b=[4,5,6]
 +print a # stay the same
 +
 +a={'first':[3.1,3.2,3.3] }
 +b=a['first']
 +b=[4,5,6]
 +print a # stay the same
 +
 +a={'first':[3.1,3.2,3.3] }
 +b=a['first']
 +b[0]=4
 +print a # changed ------- because it is a operation
 +
 +a=[1,2, [3.1,3.2,3.3] ]
 +b=a[2][:] # fast copy
 +c=[4,5,6]
 +b=c
 +print a # stay the same
 +
 +#~~~~~~~~~~~~~~~~~~~~~~~
 +# example 4: 
 +    # ref: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument
 +# explain: http://effbot.org/zone/default-values.htm
 +def foo(a=[]):
 +    a.append(5)
 +    return a
 +foo() # changed every time
 +
 +########################################
 +# reference 
 +'''
 +# ref: http://stackoverflow.com/questions/9696495/python-when-is-a-variable-passed-by-reference-and-when-by-value
 +Everything in Python is passed and assigned by value (like Java)
 +Every value in Python is a reference (pointer) to an object. 
 +Objects cannot be values. 
 +Assignment always copies the value (which is a pointer); 
 +two such pointers can thus point to the same object. 
 +Objects are never copied unless you're doing something explicit to copy them.
 +# ref: http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
 +????Everything is passed by object. Rebinding and mutating are different operations
 +'''
 +</code>
 +
 +====== Common Python syntax ======
 +
 +  * python 101 - http://www.rexx.com/~dkuhlman/python_101/python_101.html
 +
 +^comment |# |
 +^print |print |
 +^string |var="string" |
 +^long string |%%varL='''\ whatever '''%% |
 +^raw string |r"Any dangerous Character here" |
 +
 +^os navigation | ^file operation | ^string operation | ^lib | ^rename |
 +^os.chdir('/path') |cd ^tfile=open('t.txt','w') | ^str.replace('o','n') |replace ^os |system ^prefix_remove_fixed |
 +^os.getcwd() | ^tfile.write('hello') | ^str[2:6] |substring ^glob |wildchar list ^prefix_add_fixed |
 +^os.ctermid() | ^tfile.close() | ^str1+str2 |concatenate ^re |regular expression ^end_remove_fixed |
 +^os.uname() | ^tfile.read() | ^str(100) |string convert ^ | ^end_add_fixed |
 +^os.listdir('/path') | ^tfile.writeline() | ^ | ^ | ^prefix_remove_vary |
 +^os.mkdir('/path') | ^tfile.readline() | ^[a-c] [abc] |class ^ | ^ |
 +^os.removedirs('/path') | ^ | ^[%%^%% abc] |not in class ^ | ^ |
 +^os.rename(src,dst) | ^ | ^a* |may have ^ | ^ |
 +^os.getenv('HOME') | ^ | ^a+ |must have one ^ | ^ |
 +^ | ^ | ^a? |may have one ^ | ^ |
 +^ | ^ | ^a{1,3} |May 1~3 one ^ | ^ |
 +^ | ^ | ^r”str” |raw string ^ | ^ |
 +^regular expression | ^ | ^ | ^ | ^ |
 +^ | ^ | ^ | ^ | ^ |
 +http://docs.python.org/howto/regex.html#regex-howto
 +
 +
 +====== Python based tool and application ======
 +  * [[http://nadiana.com/animated-terminal-progress-bar-in-python|progress bar in terminal]]
 +
 +  * http://sphinx.pocoo.org/
 +    * Python documentation generator
 +
 +  * XML to PDF generation using Python
 +    * http://www.blog.pythonlibrary.org/2012/07/18/parsing-xml-and-creating-a-pdf-invoice-with-python/
 +
 +  * Python add-on and libs
 +    * Python image library (PIL): http://www.pythonware.com/products/pil/
 +    * Python EXIF.py: http://sourceforge.net/projects/exif-py/
 +      * demo of usage: http://snippets.dzone.com/posts/show/4089
 +    * minimal EXIF reader and writer: http://fetidcascade.com/pyexif.html#x
 +    * 50 python lib list: http://blog.sina.com.cn/s/blog_4b5039210100g8ho.html
 +
 +  * a Chinese blog on python usage: http://blog.sina.com.cn/s/articlelist_1263548705_8_1.html
 +
 +** Python web applications **
 +  * https://wiki.python.org/moin/WebFrameworks/
 +  * http://www.web2py.com/
 +  * http://webpy.org/
 +====== Python GUI ======
 +  * Python GUI option list: http://wiki.python.org/moin/GuiProgramming
 +    * web platform: PyJamas
 +    * desktop platform: QT, wxWidget
 +
 +  * GUI design:
 +    * wxGlade: http://wxglade.sourceforge.net/index.php
 +    * wxFormBuilder: http://sourceforge.net/projects/wxformbuilder/files/wxformbuilder-nightly/
 +
 +  * IDE:
 +    * Eric: http://eric-ide.python-projects.org/index.html
 +
 +====== wxPython ======
 +
 +
 +===== wxPython GUI concept quick guide =====
 +
 +  * main build block
 +    * A Frame is a top-level window.
 +
 +
 +===== wxWidget - wxPython =====
 +
 +  * download: http://www.wxpython.org/download.php
 +  * youtube video tutorial: http://wiki.wxpython.org/YouTubeVideoSeries
 +
 +
 +  * basic wxPython widget syntax and codes <code python>
 +# get wxWidget frame work,
 +import wx
 +
 +# create class, since you are doing a App or a script
 +
 +# inheriate a python window (so-called frame)
 +class myApp(wx.Frame): 
 +  # constructor, (so-called the initialize function)
 +  def __init__(self,parent,id):
 +    wx.Frame.__init__(self,parent,id,'My Window', size=(300,200))
 +    panel=wx.Panel(self)
 +    
 +    #bitmap button
 +    pic=wx.Image('img.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap()
 +    myBtn=wx.BitmapButton(panel, -1, pic, pos=(10,100))
 +    self.Bind(wx.EVT_BUTTON,self.btnAct,myBtn)
 +    myBtn.SetDefault()
 +    
 +    #slider
 +    slider=wx.Slider(panel,-1,50,1,100,pos=(10,30), size=(250,-1),style=wx.SL_AUTOTICKS | wx.SL_LABELS)
 +    slider.SetTickFreq(5,1) # interval
 +    
 +    # spinner
 +    spinner=wx.SpinCtrl(panel,-1,'',pos=(10,1), size=(90,-1))
 +    spinner.SetRange(1,100)
 +    spinner.SetValue(10)
 +    
 +    # checkbox
 +    wx.CheckBox(panel,-1,'Apples',pos=(100,1),size=(60,-1))
 +    wx.CheckBox(panel,-1,'Orange',pos=(160,1),size=(60,-1))
 +    wx.CheckBox(panel,-1,'Banana',pos=(220,1),size=(60,-1))
 +
 +    # combo box
 +    mylist=['Apple','Orange','Banana']
 +    cbox=wx.ListBox(panel,-1,(300,1),(80,60),mylist,wx.LB_SINGLE)
 +    cbox.SetSelection(0)
 +
 +# event handlers
 +  def btnAct(self,event):
 +    self.Destroy()
 +
 +if __name__=='__main__':
 +    app=wx.PySimpleApp() # application obj
 +    frame=myClass(parent=None,id=-1) # frame obj
 +    frame.Show() # show frame
 +    app.MainLoop() # start app    
 +</code>
 +  * wxWidget binding for python tutorial: http://zetcode.com/wxpython/introduction/
 +
 +  * 2 Panel App Flow
 +    * http://www.blog.pythonlibrary.org/2010/06/16/wxpython-how-to-switch-between-panels/
 +
 +===== Common wxPython syntax =====
 +
 +  * prompts <code python>
 +#import wx.lib.agw.multidirdialog as MDD
 +import multidirdialog as MDD
 +def showMsg(msg):
 +    wx.MessageBox(msg, 'Info', 
 +    wx.OK | wx.ICON_INFORMATION)
 +
 +import wx.lib.dialogs
 +def showTxt(txt):
 +    ask = wx.lib.dialogs.ScrolledMessageDialog(None, txt, "Info")
 +    ask.ShowModal()
 +    ask.Destroy()
 +
 +def getInput():
 +    tmpT=""
 +    ask = wx.TextEntryDialog(
 +            None, 'Your input:',
 +            'Input', '1')
 +    if ask.ShowModal() == wx.ID_OK:
 +        print "Input: %s\n" % ask.GetValue()
 +        tmpT = ask.GetValue()
 +    ask.Destroy()
 +    return tmpT
 +
 +def getFolder():
 +    tmpPath=""
 +    dialog = wx.DirDialog ( None, message = 'Select a directory.' )
 +    if dialog.ShowModal() == wx.ID_OK:
 +        print 'Directory:', dialog.GetPath()
 +        tmpPath = dialog.GetPath()
 +    else:
 +        print 'No directory.'
 +    dialog.Destroy()
 +    return tmpPath
 +
 +
 +def getFolders():
 +    tmpPaths=""
 +    ask = MDD.MultiDirDialog(None, title="Select folders:", defaultPath='', agwStyle=0)
 +    if ask.ShowModal() == wx.ID_OK:
 +        tmpPaths = ask.GetPaths()
 +        print "You chose the following file(s):"
 +        for path in tmpPaths:
 +            print path
 +    ask.Destroy()
 +    return tmpPaths
 +
 +def getFile():
 +    tmpPaths=""
 +    wildcard = "Python source (*.py)|*.py|" "All files (*.*)|*.*"
 +    ask = wx.FileDialog(None, message="Choose a file", defaultDir="", defaultFile="", wildcard=wildcard,style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR)
 +    if ask.ShowModal() == wx.ID_OK:
 +        tmpPaths = ask.GetPaths()
 +        print "Select file(s):"
 +        for path in tmpPaths:
 +            print path
 +    ask.Destroy()
 +    return tmpPaths
 +
 +def saveFile():
 +    tmpPaths=""
 +    wildcard = "Python source (*.py)|*.py|" "All files (*.*)|*.*"
 +    ask = wx.FileDialog(None, message="Save As", defaultDir="", defaultFile="", wildcard=wildcard,style=wx.SAVE)
 +    if ask.ShowModal() == wx.ID_OK:
 +        tmpPath = ask.GetPath()
 +        print "Save file:", tmpPath
 +    ask.Destroy()
 +    return tmpPath
 +    
 +def getColor():
 +    ask = wx.ColourDialog(None)
 +    ask.GetColourData().SetChooseFull(True)
 +    if ask.ShowModal() == wx.ID_OK:
 +        data = ask.GetColourData()
 +        print 'You selected: %s\n' % str(data.GetColour().Get(includeAlpha=False))
 +    ask.Destroy()
 +    return data.GetColour().Get(includeAlpha=False)
 +
 +def getChoice():
 +    tmpC=""
 +    ask = wx.SingleChoiceDialog(
 +        None, "Select a choice", 'The direction',
 +        ["Up", "Down", "Left", "Right"], 
 +        wx.CHOICEDLG_STYLE
 +        )
 +    if ask.ShowModal() == wx.ID_OK:
 +        tmpC = ask.GetStringSelection()
 +        print 'Choice: %s\n' % ask.GetStringSelection()
 +    ask.Destroy()
 +    return tmpC
 +
 +def getChoices():
 +    tmpCs=[]
 +    list = ["Photo", "Movie", "Docs"]
 +    ask = wx.MultiChoiceDialog( None, 
 +                               "Select the choices",
 +                               "wx.MultiChoiceDialog", list)
 +
 +    if (ask.ShowModal() == wx.ID_OK):
 +        selections = ask.GetSelections()
 +        strings = [list[x] for x in selections]
 +        print "You chose:" + str(strings)
 +        tmpCs = strings
 +    ask.Destroy()
 +    return tmpCs
 +    
 +application = wx.PySimpleApp()
 +</code>
 +  * all dialog reference: 
 +    * http://www.blog.pythonlibrary.org/2010/06/26/the-dialogs-of-wxpython-part-1-of-2/
 +    * http://www.blog.pythonlibrary.org/2010/07/10/the-dialogs-of-wxpython-part-2-of-2/
 +
 +
 +
 +====== PyQt ======
 +
 +  * check [[devwiki:python_qt|Qt and Python]]
 +====== Python Portable for Windows ======
 +
 +  * since all mac and linux have python built-in
 +
 +**Workflow on portable python**
 +
 +  - Prepare python 2.7/3.5, by download the official python exe, and install it to D drive
 +    * https://www.python.org/
 +      * for Python3.5.2: Windows x86 executable installer
 +      * for Python2.7.12: Windows x86 MSI installer
 +  - update: for PyQt and PySide, just cd to that python directory and .\python.exe -m pip install PySide or PyQt4 to install that module with pip or ./python for mac/linux
 +  - <del>Prepare PyQt4 library for py2.7/3.5 by download the official, and install to the correct PythonX.X folder</del>
 +    * <del>https://www.riverbankcomputing.com/software/pyqt/download</del>
 +      * <del>for Python3.5: PyQt4-4.11.4-gpl-Py3.4-Qt4.8.7-x32.exe</del> 
 +      * <del>for python2.7: PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe</del>
 +  - other library:
 +    * cx_Freeze
 +
 +====== Python Portable for Mac ======
 +
 +  * Python 2.7 is built-in in Mac at /System/Library/Frameworks/Python.framework/Versions/2.7/
 +  * There are many articles on install latest python 2.7 and 3.x version with many methods, like homebrew and offical python site, while my idea is not touch any system path or system environment, be portable means, the system dont even need to know my personal python app is there, like windows portable version
 +    * the advantage is you choose which python to run the py script
 +    * the disadvantage is you can't default python the_script.py in your command line, so you should always use a launch bash script to dynamically use which python path and open the script with it.
 +      * check my App_V9.bat launch for windows: https://github.com/shiningdesign/universal_tool_template.py/blob/master/App_V9.bat
 +
 +**Standard Install Python 3.8**
 +  * download and install python3 installer
 +  * download and install command line tools for xcode latest (use normal Apple acc to login)
 +    * https://developer.apple.com/download/
 +  * install PySide2 (with user flag that fixes permission issue) <code bash>pip3 install PySide2 --user</code>
 +  * now python3 work in terminal app
 +    * when first run, allow Access folder right.
 +  * to make command extension file runable, do bash for the command extension launcher <code bash>chmod 755 App_V9.command</code>
 +====== Python Cool Tricks and Notes ======
 +
 +  * start HTML and FILE server in current directory <code dos>python -m SimpleHTTPServer 8181</code>
 +  * decimal vs float for real life accounting usage:
 +    * decimal wont have those float annoying issue with tons of 0s after .
 +    * decimal deal number like human do instead of infinite computing accuracy
 +    * https://towardsdev.com/python-tips-decimal-vs-float-72fe72fb7086
 +    * https://www.laac.dev/blog/float-vs-decimal-python/
 +    * <code>from decimal import Decimal; a = Decimal(0.01)</code>
 +
 +
 +  * python and whatsapp msg
 +    * ref: https://iq.opengenus.org/python-script-to-send-whatsapp-message/
 +    * **pywhatkit**: use web client to automate msg sending, make sure it on main screen (multi screen case) 
 +      * because it use automation, so interaction with browser can be buggy, no quick repeat sending (since automate like human action, need wait response of other app update), send group chat may send to wrong group if you also interact with browser
 +      * example<code python>
 +import pywhatkit
 +pywhatkit.sendwhatmsg_instantly("+88123456",auto_url,10,True,3) 
 +# wait 10s to ui response, close tab true after 3s, 
 +# you need to close tab as whatsapp web only allow 1 window active
 +</code>
 +    * **selenium**: use selenium browser automate driver to operate, since it is linking to a dedicated browser with browser api, it is more reliable to send fast msg and maintain connection, no user interaction affection
 +      * example setup and login manually for first time <code python>
 +my_drive = None
 +def drive_setup():
 +    from selenium import webdriver
 +    from selenium.webdriver.common.keys import Keys
 +    from selenium.webdriver.common.by import By
 +    
 +    my_drive = webdriver.Chrome()
 +    phone_no = "+881234567" # target phone or your own phone as target
 +    my_drive.get(f'https://web.whatsapp.com/send?phone={phone_no}&text=')
 +
 +def drive_msg(in_text):
 +    from selenium.webdriver.common.by import By
 +    if my_drive is None:
 +        return
 +    else:
 +        chat_box = my_drive.find_element(by=By.XPATH, value='/html/body/div[1]/div/div/div[4]/div/footer/div[1]/div/span[2]/div/div[2]/div[1]/div/div/p')
 +        chat_box.send_keys(in_text)
 +        send_button = my_drive.find_element(by=By.CSS_SELECTOR, value='[data-icon="send"]')
 +        send_button.click()
 +</code>
 +
 +====== Python and interaction with other API ======
 +
 +Telegram
 +  - search @BotFather on telegram
 +    * manual: https://core.telegram.org/bots/api
 +  - in the chat, type /newbot, then type your bot "botname", then type your bot "botusername_bot"
 +  - now, your bot api key will show up, copy and save it
 +  - now search your bot "botusername_bot" to start chat with it
 +  - in python, use requests to get the chat from the bot, try one more time if no result <code python>
 +import requests
 +the_key = "api_key"
 +url = "https://api.telegram.org/bot{0}/getUpdates".format(the_key)
 +response = requests.get(url)
 +result = response.json()
 +print(result)
 +</code>
 +  - once you got result in json, you will find the chat id. <code python>result['result']
 +# the list of all chat spec your bot received, yours is likely the first one.
 +my_chat_info = result['result']
 +my_msg_info = my_chat_info[0]['message']
 +# it got info of: chat, date, from, message_id, text
 +my_chat_id = my_msg_info['chat']['id']
 +</code>
 +  - to send a text to that chat from your bot side <code python>
 +reply_text = 'hello world'
 +url = "https://api.telegram.org/bot{key}/sendMessage?chat_id={id}&text={reply}".format(**{'key':the_key, 'id':my_chat_id,'reply':reply_text})
 +response = requests.get(url)
 +</code>
 +  - now, you should have got the reply from your bot <code python>
 +result = response.json()
 +send_state = result['ok']
 +send_result = result['result']
 +# chat (first_name, id, type); date:int; message_id; text
 +</code>
 +
 +====== Python and Networking related ======
 +
 +  * above is python interact with a REST API server, you can also use python to build your own REST API server to handle requests from other application, even other application over the network and internet.
 +  * implement REST API into python, you may need to use some python module like Flash, FastAPI, then you start another thread from your python application to run your REST API server.
 +  * while using Sockets method may be simpler if you just want to send some simple message between applications, as REST API, you need write all the API URL end point response.
 +  * using REST API allow your application to talk cross different application and difference device, and provide a safe gate to your important data handled by your application.