File: android-tkinter/CODE/PyGadgets.py

"""
==============================================================================
PyGadgets: the standalone release of PyCalc, PyClock, PyPhoto, and PyToe.

#-----------------------------------------------------------------------------
# ANDROID version, Jan-Apr 2019 (see "# ANDROID" for changes)
# PARTIALLY WORKING: the gadget spawns fail due to a Pydroid 3 glitch.
#
# [Apr2119] Pydroid 3 3.0 broke webbrowser: use os.system(cmd) with a 
#           hardcoded Android activity-manager command line instead
#           (3.0's $DISPLAY breaks module, $BROWSER kills "file://").
#           Also: Buttons=>Labels like Mac OS, to avoid bg color loss bug.
#
# [Apr1919] Use new python exe path scheme for spawns to be path agnostic.
#           Note that help/more URLs already worked as is on Android,
#           but help text also requires an extra install of patched 
#           file helpmessage.py to avoid its former font crash. 
#-----------------------------------------------------------------------------

Copyright 1996-2019 M. Lutz, from book "Programming Python, 4th Edition".
License (short): provided freely, but with no warranties of any kind.
See README.txt for full license, versions, and release and usage notes.

PyGadgets' standalone release lives at learning-python.com/pygadgets.html.
It includes the gadgets and their GUI launcher, in Mac OS app, Windows
exe, Linux executable, and source-code forms.  All gadgets were ported 
to Mac OS and verified on Linux and Windows as part of this release.
This launcher script has also been changed heavily since PP4E; see "[SA]".

This script can either spawn the 4 gadgets immediately, or start a simple
launcher GUI that opens gadgets on demand easily.  For source code users:

   - Run this script itself to start all 4 gadgets at once
   - Run PyGadgets_bar.pyw to start the simple gadget-launcher GUI
   - Run the apps' main scripts listed ahead to start gadgets directly

For app/exe package users: run either the GUI-launcher bar's app/exe, 
or the individual gadgets' apps/exes directly.  Source-code users should
install a Python 3.X if needed (3.5 is the latest verified Python version);
app/exe users can run their products without a Python install.

The book's PyEdit and PyMailGUI programs are not launchable here, but are 
provided as separate applications, given their much larger size and scope.
See learning-python.com/programs.html for these programs' current releases.

The contents of the _Py* subfolders here were copied from the PP4E book's
examples package, and modified to nest their PP4E package requirements 
within themselves.  In the book, the gadgets' code is nested in the larger
PP4E package tree, reflecting their learning-resource roles.

--Original unedited PP4E comments follow--
 
Start various examples; run me at start time to make them always available.
This file is meant for starting programs you actually wish to use; see 
PyDemos for starting Python/Tk demos and more details on program start 
options.  Windows usage note: this is a '.py' to show messages in a console 
window when run or clicked (including a 10 second pause to make sure it's 
visible while gadgets start if clicked).  To avoid Windows console pop up,
run with the 'pythonw' program (not 'python'), rename to '.pyw' suffix, 
mark with 'run minimized' window property, or spawn elsewhere (see PyDemos).
==============================================================================
"""

import sys, os, time
from tkinter import *

# [SA] for frozen app/exes, fix module+resource visibility (sys.path, cwd)
# data+scripts not in os.getcwd() if run from a cmdline elsewhere,
# and __file__ may not work if running as a frozen PyInstaller executable;
# use __file__ of this file for Mac apps, not module: it's in a zipfile;
# os.chdir() = access all data relative to '.': no cmdline args to this script

import fixfrozenpaths
launcherpath = fixfrozenpaths.fetchMyInstallDir(__file__)   # absolute
os.chdir(launcherpath)

from Gui.Tools.windows import MainWindow           # reuse window tools: icon, quit

# [SA]: port to Mac too
RunningOnMac     = sys.platform.startswith('darwin')
RunningOnWindows = sys.platform.startswith('win')           # or [:3] == 'win'
RunningOnLinux   = sys.platform.startswith('linux')

# [SA]: new configurables for toolbar itself (gadgets exec() the same file)
# unlike the gadgets, the main toolbar doesn't allow configs via cmd args
from PyGadgets_configs import InitialSize, BgColor, FgColor, Font

# [SA]: set window icons on Windows and Linux
from windowicons import trySetWindowIcon


def runImmediate(mytools):
    """
    ---------------------------------------------------------------------
    launch gadget programs immediately (if this script run directly)
    ---------------------------------------------------------------------
    """
    print('Starting Python/Tk gadgets...')         # msgs to stdout (poss temp)
    for (name, scriptfile) in mytools:
        launchit(name, scriptfile)                 # call now to start now
    print('One moment please...')
    if sys.platform[:3] == 'win':                  # windows: keep console 10 secs
        for i in range(10): 
            time.sleep(1); print('.' * 5 * (i+1))


def runLauncher(mytools):
    """
    ---------------------------------------------------------------------
    pop up a simple GUI launcher toolbar for on-demand use
    ---------------------------------------------------------------------
    """
    global root
    root = MainWindow('PyGadgets')                 # or root = Tk()
    if InitialSize: root.geometry(InitialSize)     # [SA] configs, WxH
    trySetWindowIcon(root, 'icons', 'pygadgets')   # [SA] for win+lin

    # [SA] question=? but portable, help key in all gadgets
    root.bind('<KeyPress-question>', lambda event: onHelp())

    def label(root, name):
        return configure(Label(root, text=name, relief=RIDGE, border=3))

    def button(root, name):
        return configure(Button(root, text=name, border=2))

    def configure(widget):
        widget.config(fg=FgColor, bg=BgColor, font=Font)
        widget.pack(side=LEFT, expand=YES, fill=BOTH)
        return widget
       
    for (name, scriptfile) in mytools:
        if RunningOnMac or True:
            # [SA] emulate colored buttons
            #
            # ANDROID [Apr2119]: Buttons in Pydroid 3's tkinter lose their bg color
            # temporarily after a press; use Labels to workaround the bug (True).
            #
            l = label(root, name)
            l.bind('<Button-1>', lambda event, n=name, s=scriptfile: launchit(n, s))
        else:
            b = button(root, name)
            b.config(command=(lambda n=name, s=scriptfile: launchit(n, s)))

    # [SA] add link to online resources
    # ANDROID [Apr2119]: Buttons in Pydroid 3's tkinter lose their bg color (True)
    if RunningOnMac or True:
        l = label(root, '+')
        l.bind('<Button-1>', lambda event: onMore())
    else:
        b = button(root, '+')
        b.config(command=onMore)

    # [SA] add help via function call
    # ANDROID [Apr2119]: Buttons in Pydroid 3's tkinter lose their bg color (True)
    if RunningOnMac or True:
        l = label(root, '?')
        l.bind('<Button-1>', lambda event: onHelp())
    else:
        b = button(root, '?')
        b.config(command=onHelp)

    if RunningOnMac:
        # Mac  requires menus, deiconifies, focus

        # [SA] on Mac, customize app-wide automatic top-of-display menu
        from guimaker_pp4e import fixAppleMenuBar
        fixAppleMenuBar(window=root,
                        appname='PyGadgets',
                        helpaction="/?originalUrl=https%3A%2F%2Flearning-python.com%2FonHelp%2C%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520aboutaction%3DNone%2C%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520quitaction%3Droot.quit)%2520%2520%2520%2520%23%2520app-wide%2520quit%3A%2520ask%2520%2520%2520%2520%2520%2520%2520%2520%23%2520%5BSA%5D%2520reopen%2520auto%2520on%2520dock%2Fapp%2520click%2520and%2520fix%2520tk%2520focus%2520loss%2520on%2520deiconify%2520%2520%2520%2520%2520%2520%2520%2520def%2520onReopen()%3A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520root.lift()%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520root.update()%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520temp%2520%3D%2520Toplevel()%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520temp.lower()%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520temp.destroy()%2520%2520%2520%2520%2520%2520%2520%2520root.createcommand(%26%23x27%3B%3A%3Atk%3A%3Amac%3A%3AReopenApplication%26%23x27%3B%2C%2520onReopen)%2520%2520%2520%2520root.mainloop()def%2520launchit(name%2C%2520scriptfile)%3A%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520---------------------------------------------------------------------%2520%2520%2520%2520%5BSA%5D%2520new%2520launch%2520scheme%3A%2520%2Bconfigs%2C%2520drop%2520stdout%2520in%2520Mac%2520apps%2520per%2520%2520%2520%2520http%3A%2F%2Flearning-python.com%2Fbroken-pipe-workaround-aug17.html%3B%2520%2520%2520%2520table%2520entry%2520is%2520now%2520a%2520script%2C%2520not%2520a%2520full%2520cmdline%3A%2520configs%2520in%2520file%3B%2520%2520%2520%2520passes%2520a%2520sequence%2520(not%2520string)%2520to%2520defer%2520arg%2520quoting%2520to%2520subrocess%3B%2520%2520%2520%2520---------------------------------------------------------------------%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520import%2520subprocess%2520%2520%2520%2520print(name)%2520%2520%2520%2520%23%2520where%2520gadgets%2520should%2520fetch%2520their%2520configs%2520class%3A%2520exec()%2C%2520fetch%2520attr%2520%2520%2520%2520%2520configsfile%2520%3D%2520os.path.abspath(%26%23x27%3BPyGadgets_configs.py%26%23x27%3B)%2520%2520%2520%2520if%2520hasattr(sys%2C%2520%26%23x27%3Bfrozen%26%23x27%3B)%2520and%2520RunningOnMac%3A%2520%2520%2520%2520%2520%2520%2520%2520outputstream%2520%3D%2520subprocess.DEVNULL%2520%2520%2520%2520else%3A%2520%2520%2520%2520%2520%2520%2520%2520outputstream%2520%3D%2520None%2520%2520%2520%23%2520also%2520the%2520default%3A%2520inherit%2520parent%26%23x27%3Bs%2520%2520%2520%2520%2520scriptfile%2520%3D%2520os.path.abspath(scriptfile)%2520%2520%2520%2520%2520%2520%2520%2520if%2520hasattr(sys%2C%2520%26%23x27%3Bfrozen%26%23x27%3B)%2520and%2520(RunningOnWindows%2520or%2520RunningOnLinux)%3A%2520%2520%2520%2520%2520%2520%2520%2520%23%2520pyinstaller%2520exe%2520%2520%2520%2520%2520%2520%2520%2520cmdline%2520%3D%2520%5Bscriptfile%2C%2520%26%23x27%3B-configs%26%23x27%3B%2C%2520configsfile%5D%2520%2520%2520%2520else%3A%2520%2520%2520%2520%2520%2520%2520%2520%23%2520py2app%2520Mac%2520app%2520or%2520source%2520%2520%2520%2520%2520%2520%2520%2520%23%2520ANDROID%2520-%2520sys.executable%2520is%2520empty%2520in%2520Pydroid%25203%3A%2520Popen%2520fails%2520%2520%2520%2520%2520%2520%2520%2520%23%2520%2520%2520%2520%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr1919%5D%3A%2520Pydroid%25203%26%23x27%3Bs%25203.0%2520release%2520moved%2520its%2520Python%2520from%2520the%2520%2520%2520%2520%2520%2520%2520%2520%23%2520first%2520of%2520the%2520following%2520paths%2520to%2520the%2520second%2C%2520breaking%2520this%2520workaround%3A%2520%2520%2520%2520%2520%2520%2520%2520%23%2520%2520%2520%2520%2Fdata%2Fuser%2F0%2Fru.iiec.pydroid3%2Ffiles%2Farm-linux-androideabi%2Fbin%2Fpython%2520%2520%2520%2520%2520%2520%2520%2520%23%2520%2520%2520%2520%2Fdata%2Fuser%2F0%2Fru.iiec.pydroid3%2Ffiles%2Faarch64-linux-android%2Fbin%2Fpython%2520%2520%2520%2520%2520%2520%2520%2520%23%2520to%2520allow%2520for%2520both%2520paths--and%2520be%2520platform%2520agnostic%2520in%2520general--read%2520the%2520%2520%2520%2520%2520%2520%2520%2520%23%2520result%2520of%2520a%2520%26%23x27%3Bwhich%2520python%26%23x27%3B%2520shell%2520command%2520instead%2520of%2520using%2520literal%2520strs%3B%2520%2520%2520%2520%2520%2520%2520%2520%23%2520%2520%2520%2520%2520%2520%2520%2520pythonexe%2520%3D%2520sys.executable%2520%2520%2520%2520%2520%2520%2520%2520%2520pythonexe%2520%3D%2520os.popen(%26%23x27%3Bwhich%2520python%26%23x27%3B).read().rstrip()%2520%2520%23%2520path%2520to%2520Python%2520exe%2520%2520%2520%2520%2520%2520%2520%2520cmdline%2520%3D%2520%5Bpythonexe%2C%2520scriptfile%2C%2520%26%23x27%3B-configs%26%23x27%3B%2C%2520configsfile%5D%2520%2520%2520%2520%2520%23%2520%2520%2520%2520%23%2520ANDROID%3A%2520no%2520combination%2520of%2520Popen%2520arguments%2520or%2520DISPLAY%2520settings%2520%2520%2520%2520%2520%23%2520has%2520been%2520found%2520to%2520make%2520this%2520work%2520to%2520date%2520(suggestions%2520welcome)%3B%2520%2520%2520%2520%23%2520gadgets%2520can%2520write%2520to%2520files%2C%2520which%2520means%2520spawnees%2520are%2520started...%2520%2520%2520%2520%23%2520%2520%2520%2520print(cmdline)%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%23%2520ANDROID%2520-%2520tracing%2520%2520%2520%2520subprocess.Popen(cmdline%2C%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%23%2520seq%3A%2520quotes%2520args%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520stdout%3Doutputstream%2C%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%23%2520fix%2520mac%2520app%2520stdout%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520stderr%3Doutputstream)def%2520onMore()%3A%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520---------------------------------------------------------------------%2520%2520%2520%2520%5BSA%5D%2520online%2520resources%2520(no%2520longer%2520via%2520LanchBrowser%2520command%2520line)%3B%2520%2520%2520%2520---------------------------------------------------------------------%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr1919%5D%3A%2520this%2520works%2520as%2520is%2520-%2520already%2520using%2520online%2520URL%2520for%2520HTML%3B%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr2119%5D%3A%2520not%2520anymore%2520in%2520Pydroid%25203%25203.0%3A%2520use%2520os.system%2520instead%3B%2520%2520%2520%2520%23%2520%2520%2520%2520brw%2520%3D%2520%26%23x27%3Bam%2520start%2520--user%25200%2520-a%2520android.intent.action.VIEW%2520-d%2520%25s%26%23x27%3B%2520%2520%2520%2520url%2520%3D%2520%26%23x27%3Bhttp%3A%2F%2Flearning-python.com%2Fprograms.html%26%23x27%3B%2520%2520%2520%2520cmd%2520%3D%2520brw%2520%25%2520url%2520%2520%2520%2520os.system(cmd)%2520%2520%2520%2520%23%2520other%2520platforms%2520code...%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520import%2520webbrowser%2520%2520%2520%2520webbrowser.open(%26%23x27%3Bhttp%3A%2F%2Flearning-python.com%2Fprograms.html%26%23x27%3B)%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3Bdef%2520onHelp()%3A%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520---------------------------------------------------------------------%2520%2520%2520%2520%5BSA%5D%2520simple%2520help%2520info%2520display%2C%2520for%2520the%2520GUI%2520launcher%2520only%3B%2520%2520%2520%2520%2520called%2520for%2520%26%23x27%3B%3F%26%23x27%3B%2520button%2520click%2C%2520%26%23x27%3B%3F%26%23x27%3B%2520keyboard%2520press%2C%2520and%2520Mac%2520menus%3B%2520%2520%2520%2520---------------------------------------------------------------------%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520%23%2520%5BSep-2018%5D%3A%2520popup%2520top-level%2520readme%2520file%2520too%2520%2520%2520%2520%23%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr1919%5D%3A%2520this%2520works%2520as%2520is%2520-%2520already%2520using%2520%26quot%3Bfile%3A%2F%2F%26quot%3B%2520prefix%3B%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr2119%5D%3A%2520not%2520anymore%2520in%2520Pydroid%25203%25203.0%3A%2520use%2520os.system%2520instead%3B%2520%2520%2520%2520%23%2520%2520%2520%2520brw%2520%3D%2520%26%23x27%3Bam%2520start%2520--user%25200%2520-a%2520android.intent.action.VIEW%2520-d%2520%25s%26%23x27%3B%2520%2520%2520%2520url%2520%3D%2520%26%23x27%3Bfile%3A%2F%2F%26%23x27%3B%2520%2B%2520os.path.abspath(%26%23x27%3BREADME.txt%26%23x27%3B)%2520%2520%2520%2520cmd%2520%3D%2520brw%2520%25%2520url%2520%2520%2520%2520os.system(cmd)%2520%2520%2520%2520%23%2520other%2520platforms%2520code...%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520try%3A%2520%2520%2520%2520%2520%2520%2520%2520import%2520webbrowser%2520%2520%2520%2520%2520%2520%2520%2520webbrowser.open_new(%26%23x27%3Bfile%3A%2F%2F%26%23x27%3B%2520%2B%2520os.path.abspath(%26%23x27%3BREADME.txt%26%23x27%3B))%2520%2520%2520%2520%2520%2520%2520%2520%23time.sleep(1.0)%2520%2520%2520%2520%2520%2520%2520%2520root.focus_force()%2520%2520%2520%2520%23%2520get%2520back%2520focus%2C%2520for%2520info%2520popup%2520%2520%2520%2520except%3A%2520%2520%2520%2520%2520%2520%2520%2520pass%2520%2520%23%2520don%26%23x27%3Bt%2520care%2520(and%2520PyEdit%2520is%2520not%2520in%2520the%2520bundle)%2520%2520%2520%2520%26quot%3B%26quot%3B%26quot%3B%2520%2520%2520%2520%23%2520original%2520GUI%2520popup%2520%2520%2520%2520%23%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr1919%5D%3A%2520this%2520REQUIRES%2520the%2520new%2520patched%2520version%2520%2520%2520%2520%2520%23%2520of%2520helpmessage.py%2C%2520to%2520avoid%2520font%2520crash%2520in%2520Pydroid%25203%25202.2%2C%2520%2520%2520%2520%2520%23%2520and%2520use%2520intended%2520font%2520in%2520Pydroid%25203%25203.0%2520(else%2520helvetica).%2520%2520%2520%2520%23%2520Caveat%3A%2520Android%26%23x27%3Bs%2520scrolled-text%2520popup%2520loses%2520focus%2520after%2520%2520%2520%2520%2520%23%2520README%2520activity%2C%2520and%2520update()%2520and%2520focus_force()%2520don%26%23x27%3Bt%2520help.%2520%2520%2520%2520%23%2520%2520%2520%2520from%2520helpmessage%2520import%2520showhelp%2520%2520%2520%2520showhelp(root%2C%2520%26%23x27%3BPyGadgets%26%23x27%3B%2C%2520HelpText%2C%2520forcetext%3DFalse%2C%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520setwinicon%3Dlambda%2520win%3A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520trySetWindowIcon(win%2C%2520%26%23x27%3Bicons%26%23x27%3B%2C%2520%26%23x27%3Bpygadgets%26%23x27%3B))%2520%2520%2520%2520%23root.focus_force()%2520%2520%2520%23%2520now%2520done%2520in%2520helpmessageHelpText%2520%3D%2520(%26%23x27%3BPyGadgets%25204.3%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BGUI%2520toys%2520%25E2%2580%2594%2520just%2520for%2520the%2520hack%2520of%2520it.%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BA%2520set%2520of%2520desktop%2520GUI%2520utilities%2C%2520coded%2520in%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BPython%2Ftkinter%2C%2520and%2520available%2520for%2520Mac%2520OS%2C%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BWindows%2C%2520Linux%2C%2520and%2520Android.%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BStandalone%2520release%2520of%2520programs%2520originally%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bfrom%2520the%2520book%2520Programming%2520Python.%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BAuthor%2520and%2520%25C2%25A9%2520M.%2520Lutz%25201996-2019.%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BDOCS%3A%2520see%2520the%2520top-level%2520README.txt%2520file%2520in%2520your%2520install%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bpackage%2520for%2520general%2520usage%2520details%2C%2520and%2520see%2520each%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bgadget%5C%26%23x27%3Bs%2520in-program%2520help%2520(press%2520%26quot%3B%3F%26quot%3B%2520in%2520any%2520gadget%2C%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bor%2520use%2520its%2520menu%2520or%2520%26quot%3Bhelp%26quot%3B%2520or%2520%26quot%3B%3F%26quot%3B%2520widgets%2520if%2520available).%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BFILES%3A%2520on%2520Macs%2C%2520access%2520your%2520install%2520package%2520by%2520Show%2520Package%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BContents%2520on%2520the%2520app%2C%2520then%2520go%2520to%2520Contents%2FResources.%2520%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BOn%2520other%2520platforms%2C%2520your%2520install%2520package%2520is%2520the%2520folder%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bcreated%2520by%2520unzipping%2520the%2520download.%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BCUSTOMIZATIONS%3A%2520to%2520customize%2520both%2520PyGadgets%2520itself%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Band%2520each%2520of%2520the%2520gadgets%2520it%2520starts%2C%2520edit%2520file%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BPyGadgets_configs.py%2520at%2520the%2520top%2520level%2520of%2520your%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Binstall%2520package.%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BREQUIREMENTS%3A%2520PyGadgets%2520apps%2520and%2520executables%2520require%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bno%2520extra%2520installs.%2520%2520Source%2520code%2520requires%2520installs%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bof%2520Python%25203.X%2C%2520Tk%2C%2520and%2520Pillow%2520for%2520PyPhoto%2520(always)%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Band%2520PyClock%2520(in%2520some%2520use%2520cases).%2520%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BSee%2520README.txt%2520and%2520gadgets%5C%26%23x27%3B%2520help%2520for%2520details.%2520%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%23%2520ANDROID%2520%5BApr2119%5D%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BANDROID%2520USERS%3A%2520the%2520launcher%2520is%2520not%2520currently%2520%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Boperational%3B%2520run%2520individual%2520gadget%2520scripts%2520directly.%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%5Cn%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3BFor%2520downloads%2520and%2520more%2520apps%2C%2520visit%3A%5Cn%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3Bhttp%3A%2F%2Flearning-python.com%2Fprograms.html%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520)%23------------------------------------------------------------------------%23%2520the%2520gadgets%2520(name%2C%2520scriptfile)%2520launched%2520here%23%2520%5BSA%5D%2520new%2520standalone%2520model%2520required%2520new%2520launches%2520and%2520configs%3B%23%2520new%2520%26%23x27%3B%3F%26%23x27%3B%2520help%2520is%2520a%2520function%2520call%3B%2520for%2520%26%23x27%3B%3F%26%23x27%3B%2520more%2C%2520drop%2520LaunchBrowser.pyw%23%2520so%2520don%26%23x27%3Bt%2520need%2520to%2520freeze%2520it%2520for%2520Win%2FLin%2520exes%2520(use%2520webbrowser%2520directly).%23------------------------------------------------------------------------if%2520hasattr(sys%2C%2520%26%23x27%3Bfrozen%26%23x27%3B)%2520and%2520(RunningOnWindows%2520or%2520RunningOnLinux)%3A%2520%2520%2520%2520%23%2520Windows%2520and%2520Linux%2520PyInstaller%2520exe%3A%2520gadgets%2520are%2520exes%2520too%2520%2520%2520%2520mytools%2520%3D%2520%5B%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyCalc%26%23x27%3B%2C%2520%2520%2520%26%23x27%3Bcalculator%26%23x27%3B)%2C%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyClock%26%23x27%3B%2C%2520%2520%26%23x27%3Bclock%26%23x27%3B)%2C%2520%2520%2520%2520%2520%2520%2520%2520%2520%23%2520.exe%2520optional%2520on%2520Windows%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyPhoto%26%23x27%3B%2C%2520%2520%26%23x27%3Bpyphoto%26%23x27%3B)%2C%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyToe%26%23x27%3B%2C%2520%2520%2520%2520%26%23x27%3Btictactoe%26%23x27%3B)%2C%2520%2520%2520%2520%5Delse%3A%2520%2520%2520%2520%23%2520Mac%2520app%2520or%2520source%2520code%3A%2520gadget%2520scripts%2520run%2520with%2520available%2520Python%2520%2520%2520%2520%2520mytools%2520%3D%2520%5B%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyCalc%26%23x27%3B%2C%2520%2520%2520%26%23x27%3B_PyCalc%2FCalculator%2Fcalculator.py%26%23x27%3B)%2C%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyClock%26%23x27%3B%2C%2520%2520%26%23x27%3B_PyClock%2FClock%2Fclock.py%26%23x27%3B)%2C%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyPhoto%26%23x27%3B%2C%2520%2520%26%23x27%3B_PyPhoto%2FPIL%2Fpyphoto.py%26%23x27%3B)%2C%2520%2520%2520%2520%2520%2520%2520%2520(%26%23x27%3BPyToe%26%23x27%3B%2C%2520%2520%2520%2520%26%23x27%3B_PyToe%2FTicTacToe%2Ftictactoe.py%26%23x27%3B)%2C%2520%2520%2520%2520%5D%23------------------------------------------------------------------------%23%2520DEFUNCT%2520retained%2520for%2520reference%2520only)%3A%2520%23%2520original%2520PP4E%2520code%2520-%2520still%2520works%2520for%2520source%2C%2520but%2520limited%2C%2520unmaintained%3B%23------------------------------------------------------------------------%26quot%3B%26quot%3B%26quot%3Bmytools%2520%3D%2520%5B%2520%2520%2520%2520%23%2520%5BSA%5D%2520new%2520paths%2520%2520%2520%2520(%26%23x27%3BPyCalc%26%23x27%3B%2C%2520%2520%2520%26%23x27%3B_PyCalc%2FCalculator%2Fcalculator.py%26%23x27%3B)%2C%2520%2520%2520%2520(%26%23x27%3BPyClock%26%23x27%3B%2C%2520%2520%26%23x27%3B_PyClock%2FClock%2Fclock.py%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%2520-size%2520200%2520-bg%2520white%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%2520-picture%2520Gui%2Fgifs%2FpythonPowered.gif%26%23x27%3B)%2C%2520%2520%2520%2520(%26%23x27%3BPyPhoto%26%23x27%3B%2C%2520%2520%26%23x27%3B_PyPhoto%2FPIL%2Fpyphoto.py%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%2520_PyPhoto%2FPIL%2Fimages%26%23x27%3B)%2C%2520%2520%2520%2520(%26%23x27%3BPyToe%26%23x27%3B%2C%2520%2520%2520%2520%26%23x27%3B_PyToe%2FTicTacToe%2Ftictactoe.py%26%23x27%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26%23x27%3B%2520-mode%2520Minimax%2520-fg%2520white%2520-bg%2520navy%26%23x27%3B)%2C%2520%2520%2520%2520%23%2520PyEdit%2520and%2520PyMailGUI%2520are%2520now%2520available%2520separately%2520%2520%2520%2520(%26%23x27%3B%2B%26%23x27%3B%2C%2520%26%23x27%3BLaunchBrowser.pyw%2520-live%2520programs.html%2520learning-python.com%26%23x27%3B)%5D%26quot%3B%26quot%3B%26quot%3B%23------------------------------------------------------------------------%23%2520main%3A%2520launch%2520gadgets%2520if%2520any%2520args%2C%2520else%2520open%2520GUI%2520toolbar%2520(clicks%2C%2520apps)%3B%23%23%2520%5BSA%5D%2520dumped%2520the%2520original%2520PyGadgets_bar.pyw%2520hook%2C%2520because%2520it%2520complicated%2520%23%2520builds%2520of%2520apps%2520and%2520exes%2520(and%2520seemed%2520too%2520indirect%2520in%2520any%2520event)%3B%2520that%23%2520file%2520was%2520retained%2520only%2520for%2520avoiding%2520a%2520console%2520popup%2520on%2520Windows.%23%23%2520%5BSA%5D%2520in%2520some%2520contexts%2C%2520Mac%2520OS%2520may%2520spawn%2520this%2520with%2520a%2520bogus%2520argument%2520%23%2520when%2520run%2520the%2520first%2520time%2520as%2520an%2520app%2520(see%2520PyEdit)%3A%2520don%26%23x27%3Bt%2520runImmediate.%23------------------------------------------------------------------------if%2520__name__%2520%3D%3D%2520%26%23x27%3B__main__%26%23x27%3B%3A%2520%2520%2520%2520prestart%2520%3D%2520len(sys.argv)%2520%26gt%3B%25201%2520%2520%2520%2520ismacapp%2520%3D%2520hasattr(sys%2C%2520%26%23x27%3Bfrozen%26%23x27%3B)%2520and%2520sys.frozen%2520%3D%3D%2520%26%23x27%3Bmacosx_app%26%23x27%3B%2520%2520%2520%2520if%2520prestart%2520and%2520not%2520ismacapp%3A%2520%2520%2520%2520%2520%2520%2520%2520runImmediate(mytools)%2520%2520%2520%2520%2520%2520%23%2520when%2520run%2520with%2520any%2520args%3A%2520PyGadgets.py%2520-%2520%2520%2520%2520else%3A%2520%2520%2520%2520%2520%2520%2520%2520runLauncher(mytools)%2520%2520%2520%2520%2520%2520%2520%23%2520when%2520run%2520with%2520no%2520args%3A%2520%2520PyGadgets.py%253C%2FPRE">



[Home page] Books Code Blog Python Author Train Find ©M.Lutz