#!/usr/bin/python3
from __future__ import annotations

import calendar
import configparser
import copy
import ctypes
import datetime
import inspect
import itertools
import json
import os
import platform
import pprint
import pydoc
import random
import socket
import subprocess
import sys
import textwrap
import threading
import time
import tkinter as tk
import tkinter.font
import traceback
import urllib.error
import urllib.parse
import warnings
from functools import wraps
from math import fabs
from tkinter import filedialog  # noqa
from tkinter import ttk
from tkinter.colorchooser import askcolor  # noqa
from typing import Any  # noqa
from typing import Dict  # noqa
from typing import List  # noqa
from typing import Tuple  # noqa


# get the tkinter detailed version
tclversion_detailed = tkinter.Tcl().eval('info patchlevel')
framework_version = tclversion_detailed

version = __version__ = '5.1.0'

_change_log = ''


# The shortened version of version
try:
    ver = version.split(' ')[0]
except:
    ver = ''

# __version__ = version
try:
    import webbrowser

    webbrowser_available = True
except:
    webbrowser_available = False


port = 'FreeSimpleGUI'
name = 'FreeSimpleGui'


warnings.simplefilter('always', UserWarning)

g_time_start = 0
g_time_end = 0
g_time_delta = 0

# ----====----====----==== Constants the user CAN safely change ====----====----====----#

# Base64 encoded GIF file
DEFAULT_BASE64_ICON = b'R0lGODlhIQAgAPcAAAAAADBpmDBqmTFqmjJrmzJsnDNtnTRrmTZtmzZumzRtnTdunDRunTRunjVvnzdwnzhwnjlxnzVwoDZxoTdyojhzozl0ozh0pDp1pjp2pjp2pzx0oj12pD52pTt3qD54pjt4qDx4qDx5qTx5qj16qj57qz57rD58rT98rkB4pkJ7q0J9rEB9rkF+rkB+r0d9qkZ/rEl7o0h8p0x9pk5/p0l+qUB+sEyBrE2Crk2Er0KAsUKAskSCtEeEtUWEtkaGuEiHuEiHukiIu0qKu0mJvEmKvEqLvk2Nv1GErVGFr1SFrVGHslaHsFCItFSIs1COvlaPvFiJsVyRuWCNsWSPsWeQs2SQtGaRtW+Wt2qVuGmZv3GYuHSdv3ievXyfvV2XxGWZwmScx2mfyXafwHikyP7TPP/UO//UPP/UPf/UPv7UP//VQP/WQP/WQf/WQv/XQ//WRP7XSf/XSv/YRf/YRv/YR//YSP/YSf/YSv/ZS//aSv/aS/7YTv/aTP/aTf/bTv/bT//cT/7aUf/cUP/cUf/cUv/cU//dVP/dVf7dVv/eVv/eV//eWP/eWf/fWv/fW/7cX/7cYf7cZP7eZf7dav7eb//gW//gXP/gXf/gXv/gX//gYP/hYf/hYv/iYf/iYv7iZP7iZf/iZv/kZv7iaP/kaP/ka//ma//lbP/lbv/mbP/mbv7hdP7lcP/ncP/nc//ndv7gef7gev7iff7ke/7kfv7lf//ocf/ocv/odP/odv/peP/pe//ofIClw4Ory4GszoSszIqqxI+vyoSv0JGvx5OxyZSxyZSzzJi0y5m2zpC10pi715++16C6z6a/05/A2qHC3aXB2K3I3bLH2brP4P7jgv7jh/7mgf7lhP7mhf7liv/qgP7qh/7qiP7rjf7sjP7nkv7nlv7nmP7pkP7qkP7rkv7rlv7slP7sl/7qmv7rnv7snv7sn/7un/7sqv7vq/7vrf7wpv7wqf7wrv7wsv7wtv7ytv7zvP7zv8LU48LV5c3a5f70wP7z0AAAACH5BAEAAP8ALAAAAAAhACAAAAj/AP8JHEiwoMGDCA1uoYIF4bhK1vwlPOjlQICLApwVpFTGzBk1siYSrCLgoskFyQZKMsOypRyR/GKYnBkgQbF/s8603KnmWkIaNIMaw6lzZ8tYB2cIWMo0KIJj/7YV9XgGDRo14gpOIUBggNevXpkKGCDsXySradSoZcMmDsFnDxpEKEC3bl2uXCFQ+7emjV83bt7AgTNroJINAq0wWBxBgYHHdgt0+cdnMJw5c+jQqYNnoARkAx04kPEvS4PTqBswuPIPUp06duzcuYMHT55wAjkwEahsQgqBNSQIHy582D9BePTs2dOnjx8/f1gJ9GXhRpTqApFQoDChu3cOAps///9D/g+gQvYGjrlw4cU/fUnYX6hAn34HgZMABQo0iJB/Qoe8UxAXOQiEg3wIXvCBQLUU4mAhh0R4SCLqJOSEBhhqkAEGHIYgUDaGICIiIoossogj6yBUTQ4htNgiCCB4oIJAtJTIyI2MOOLIIxMtQQIJIwQZpAgwCKRNI43o6Igll1ySSTsI7dOECSaUYOWVKwhkiyVMYuJlJpp0IpA6oJRTkBQopHnCmmu2IBA2mmQi5yZ0fgJKPP+0IwoooZwzkDQ2uCCoCywUyoIW/5DDyaKefOLoJ6LU8w87pJgDTzqmDNSMDpzqYMOnn/7yTyiglBqKKKOMUopA7JgCy0DdeMEjUDM71GqrrcH8QwqqqpbiayqToqJKLwN5g45A0/TAw7LL2krGP634aoopp5yiiiqrZLuKK+jg444uBIHhw7g+MMsDFP/k4wq22rririu4xItLLriAUxAQ5ObrwzL/0PPKu7fIK3C8uxz0w8EIIwzMP/cM7HC88hxEzBBCBGGxxT8AwQzDujws7zcJQVMEEUKUbPITAt1D78OSivSFEUXEXATKA+HTscC80CPSQNGEccQRYhjUDzfxcjPPzkgnLVBAADs='

DEFAULT_BASE64_ICON_16_BY_16 = b'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKCSURBVDhPVZNbSFRRFIb35YwXItBIGtDsiqENEUTRjJlZkJggPSUYBD0UhULElE6hBY6ID/ZSpD1IDxaCEPhUaFLRQyWRNxIJe8syMxCjMCbB07fOsaMt+GftvWf//7/2Whyt1sTei/fCpDqQBTrGOi9Myrk7URwhnQUfQLeOvErJuUQgADlK6gObvAOl5sHx0doHljwARFRiCpxG5J1sjPxALiYNgn9kiQ3gafdYUYzseCd+FICX7sShw7LR++q6cl3XHaXQHFdOJLxFsJtvKHnbUr1nqp01hhStpXAzo7TZZXOjJ+9orT9pY74aY3ZobZZYW8D/GpjM19Ob088fmJxW2tkC4AJt17Oeg2MLrHX6jXWes16w1sbBkrFWBTB2nTLpv5VJg7wGNhRDwCS0tR1cbECkidwMQohAdoScqiz8/FCZUKlPCgSWlQ71elOI1fcco9hCXp1kS7dX3u+qVOm2L4nW8qE4Neetvl8v83NOb++9703BcUI/cU3imuWV7JedKtv5LdFaMRzHLW+N+zJoVDZzRLj6SFNfPlMYwy5bDiRcCojmz15tKx+6hKPv7LvjrG/Q2RoOwjSyzNDlahyzA2dAJeNtFcMHA2cfLn24STNr6P4I728jJ7hvf/lEGuaXLnkRAp0PyFK+hlyLSJGyGWnKyeBi2oJU0IPIjNd15uuL2f2PJgueQBKhVRETCgNeYU+xaeEpnWaw8cQPRM7g/McT8eF0De9u7P+49TqXF7no98BDEEkdvvXem8LAtfJniFRB/A5XeiAiG2+/icgHVQUW5d5KyAhl3M2y+U+ysv1FDukyKGQW3Y+vHJWvU7mz8RJSPZgDd3H2RqiUUn8BSQuaBvGjGpsAAAAASUVORK5CYII='

DEFAULT_BASE64_LOADING_GIF = b' '

# Old debugger logo
# PSG_DEBUGGER_LOGO = b'R0lGODlhMgAtAPcAAAAAADD/2akK/4yz0pSxyZWyy5u3zZ24zpW30pG52J250J+60aC60KS90aDC3a3E163F2K3F2bPI2bvO3rzP3qvJ4LHN4rnR5P/zuf/zuv/0vP/0vsDS38XZ6cnb6f/xw//zwv/yxf/1w//zyP/1yf/2zP/3z//30wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAyAC0AAAj/AP8JHEiwoMGDCBMqXMiwoUOFAiJGXBigYoAPDxlK3CigwUGLIAOEyIiQI8cCBUOqJFnQpEkGA1XKZPlPgkuXBATK3JmRws2bB3TuXNmQw8+jQoeCbHj0qIGkSgNobNoUqlKIVJs++BfV4oiEWalaHVpyosCwJidw7Sr1YMQFBDn+y4qSbUW3AiDElXiWqoK1bPEKGLixr1jAXQ9GuGn4sN22Bl02roo4Kla+c8OOJbsQM9rNPJlORlr5asbPpTk/RP2YJGu7rjWnDm2RIQLZrSt3zgp6ZmqwmkHAng3ccWDEMe8Kpnw8JEHlkXnPdh6SxHPILaU/dp60LFUP07dfRq5aYntohAO0m+c+nvT6pVMPZ3jv8AJu8xktyNbw+ATJDtKFBx9NlA20gWU0DVQBYwZhsJMICRrkwEYJJGRCSBtEqGGCAQEAOw=='

PSG_DEBUGGER_LOGO = b' '

UDEMY_ICON = b' '

BLANK_BASE64 = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAANSURBVBhXY2BgYGAAAAAFAAGKM+MAAAAAAElFTkSuQmCC'
BLANK_BASE64 = b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='


DEFAULT_WINDOW_ICON = DEFAULT_BASE64_ICON

DEFAULT_ELEMENT_SIZE = (45, 1)  # In CHARACTERS
DEFAULT_BUTTON_ELEMENT_SIZE = (10, 1)  # In CHARACTERS
DEFAULT_MARGINS = (10, 5)  # Margins for each LEFT/RIGHT margin is first term
DEFAULT_ELEMENT_PADDING = (5, 3)  # Padding between elements (row, col) in pixels
DEFAULT_AUTOSIZE_TEXT = True
DEFAULT_AUTOSIZE_BUTTONS = True
DEFAULT_FONT = ('Helvetica', 10)
DEFAULT_TEXT_JUSTIFICATION = 'left'
DEFAULT_BORDER_WIDTH = 1
DEFAULT_AUTOCLOSE_TIME = 3  # time in seconds to show an autoclose form
DEFAULT_DEBUG_WINDOW_SIZE = (80, 20)
DEFAULT_WINDOW_LOCATION = (None, None)
MAX_SCROLLED_TEXT_BOX_HEIGHT = 50
DEFAULT_TOOLTIP_TIME = 400
DEFAULT_TOOLTIP_OFFSET = (0, -20)
DEFAULT_KEEP_ON_TOP = None
DEFAULT_SCALING = None
DEFAULT_ALPHA_CHANNEL = 1.0
DEFAULT_HIDE_WINDOW_WHEN_CREATING = True
TOOLTIP_BACKGROUND_COLOR = '#ffffe0'
TOOLTIP_FONT = None
DEFAULT_USE_BUTTON_SHORTCUTS = False
#################### COLOR STUFF ####################
BLUES = ('#082567', '#0A37A3', '#00345B')
PURPLES = ('#480656', '#4F2398', '#380474')
GREENS = ('#01826B', '#40A860', '#96D2AB', '#00A949', '#003532')
YELLOWS = ('#F3FB62', '#F0F595')
TANS = ('#FFF9D5', '#F4EFCF', '#DDD8BA')
NICE_BUTTON_COLORS = (
    (GREENS[3], TANS[0]),
    ('#000000', '#FFFFFF'),
    ('#FFFFFF', '#000000'),
    (YELLOWS[0], PURPLES[1]),
    (YELLOWS[0], GREENS[3]),
    (YELLOWS[0], BLUES[2]),
)

COLOR_SYSTEM_DEFAULT = '1234567890'  # A Magic Number kind of signal to PySimpleGUI that the color should not be set at all
DEFAULT_BUTTON_COLOR = ('white', BLUES[0])  # Foreground, Background (None, None) == System Default
OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR = ('white', BLUES[0])

# The "default PySimpleGUI theme"
OFFICIAL_PYSIMPLEGUI_THEME = CURRENT_LOOK_AND_FEEL = 'Dark Blue 3'

DEFAULT_ERROR_BUTTON_COLOR = ('#FFFFFF', '#FF0000')
DEFAULT_BACKGROUND_COLOR = None
DEFAULT_ELEMENT_BACKGROUND_COLOR = None
DEFAULT_ELEMENT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR = None
DEFAULT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_INPUT_ELEMENTS_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_INPUT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_SCROLLBAR_COLOR = None

SYSTEM_TRAY_WIN_MARGINS = 160, 60  # from right edge of screen, from bottom of screen
SYSTEM_TRAY_MESSAGE_MAX_LINE_LENGTH = 50
SYSTEM_TRAY_MESSAGE_WIN_COLOR = '#282828'
SYSTEM_TRAY_MESSAGE_TEXT_COLOR = '#ffffff'
SYSTEM_TRAY_MESSAGE_DISPLAY_DURATION_IN_MILLISECONDS = 3000  # how long to display the window
SYSTEM_TRAY_MESSAGE_FADE_IN_DURATION = 1000  # how long to fade in / fade out the window
EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED = '__DOUBLE_CLICKED__'
EVENT_SYSTEM_TRAY_ICON_ACTIVATED = '__ACTIVATED__'
EVENT_SYSTEM_TRAY_MESSAGE_CLICKED = '__MESSAGE_CLICKED__'
_tray_icon_error = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAADlAAAA5QGP5Zs8AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAIpQTFRF////20lt30Bg30pg4FJc409g4FBe4E9f4U9f4U9g4U9f4E9g31Bf4E9f4E9f4E9f4E9f4E9f4FFh4Vdm4lhn42Bv5GNx5W575nJ/6HqH6HyI6YCM6YGM6YGN6oaR8Kev9MPI9cbM9snO9s3R+Nfb+dzg+d/i++vt/O7v/fb3/vj5//z8//7+////KofnuQAAABF0Uk5TAAcIGBktSYSXmMHI2uPy8/XVqDFbAAAA8UlEQVQ4y4VT15LCMBBTQkgPYem9d9D//x4P2I7vILN68kj2WtsAhyDO8rKuyzyLA3wjSnvi0Eujf3KY9OUP+kno651CvlB0Gr1byQ9UXff+py5SmRhhIS0oPj4SaUUCAJHxP9+tLb/ezU0uEYDUsCc+l5/T8smTIVMgsPXZkvepiMj0Tm5txQLENu7gSF7HIuMreRxYNkbmHI0u5Hk4PJOXkSMz5I3nyY08HMjbpOFylF5WswdJPmYeVaL28968yNfGZ2r9gvqFalJNUy2UWmq1Wa7di/3Kxl3tF1671YHRR04dWn3s9cXRV09f3vb1fwPD7z9j1WgeRgAAAABJRU5ErkJggg=='
_tray_icon_success = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAEKAAABCgEWpLzLAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAHJQTFRF////ZsxmbbZJYL9gZrtVar9VZsJcbMRYaMZVasFYaL9XbMFbasRZaMFZacRXa8NYasFaasJaasFZasJaasNZasNYasJYasJZasJZasJZasJZasJZasJYasJZasJZasJZasJZasJaasJZasJZasJZasJZ2IAizQAAACV0Uk5TAAUHCA8YGRobHSwtPEJJUVtghJeYrbDByNjZ2tvj6vLz9fb3/CyrN0oAAADnSURBVDjLjZPbWoUgFIQnbNPBIgNKiwwo5v1fsQvMvUXI5oqPf4DFOgCrhLKjC8GNVgnsJY3nKm9kgTsduVHU3SU/TdxpOp15P7OiuV/PVzk5L3d0ExuachyaTWkAkLFtiBKAqZHPh/yuAYSv8R7XE0l6AVXnwBNJUsE2+GMOzWL8k3OEW7a/q5wOIS9e7t5qnGExvF5Bvlc4w/LEM4Abt+d0S5BpAHD7seMcf7+ZHfclp10TlYZc2y2nOqc6OwruxUWx0rDjNJtyp6HkUW4bJn0VWdf/a7nDpj1u++PBOR694+Ftj/8PKNdnDLn/V8YAAAAASUVORK5CYII='
_tray_icon_halt = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAANswNuMPDO8HBO8FCe0HCu4IBu4IB+oLDeoLDu8JC+wKCu4JDO4LDOwKEe4OEO4OEeUQDewQDe0QDucVEuYcG+ccHOsQFuwWHe4fH/EGAvMEBfMFBvAHBPMGBfEGBvYCAfYDAvcDA/cDBPcDBfUDBvYEAPYEAfYEAvYEA/QGAPQGAfQGAvYEBPUEBvYFB/QGBPQGBfQHB/EFCvIHCPMHCfIHC/IFDfMHDPQGCPQGCfQGCvEIBPIIBfAIB/UIB/QICPYICfoBAPoBAfoBAvsBA/kCAPkCAfkCAvkCA/oBBPkCBPkCBfkCBvgCB/gEAPkEAfgEAvkEA/gGAfkGAvkEBPgEBfkEBv0AAP0AAfwAAvwAA/wCAPwCAfwCAvwCA/wABP0ABfwCBfwEAPwFA/ASD/ESFPAUEvAUE/EXFvAdH+kbIOobIeofIfEfIOcmKOohIukgJOggJesiKuwiKewoLe0tLO0oMOQ3OO43Oew4OfAhIPAhIfAiIPEiI+dDRe9ES+lQTOdSWupSUOhTUehSV+hUVu1QUO1RUe1SV+tTWe5SWOxXWOpYV+pZWelYXexaW+xaXO9aX+lZYeNhYOxjZ+lna+psbOttbehsbupscepucuxtcuxucep3fet7e+p/ffB6gOmKiu2Iie2Sk+2Qle2QluySlOyTleuYmvKFivCOjgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIxGNZsAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACVElEQVQ4T22S93PTMBhADQdl791SSsuRARTKKHsn+STZBptAi6zIacous+w9yyxl7z1T1h8ptHLhrrzLD5+/987R2XZElZ/39tZsbGg42NdvF4pqcGMs4XEcozAB/oQeu6wGr5fkAZcKOUIIRgQXR723wgaXt/NSgcwlO1r3oARkATfhbmNMMCnlMZdz5J8RN9fVhglS5JA/pJUOJiYXoShCkz/flheDvpzlBCBmya5KcDG1sMSB+r/VQtG+YoFXlwN0Us4yeBXujPmWCOqNlVwX5zHntLH5iQ420YiqX9pqTZFSCrBGBc+InBUDAsbwLRlMC40fGJT8YLRwfnhY3v6/AUtDc9m5z0tRJBOAvHUaFchdY6+zDzEghHv1tUnrNCaIOw84Q2WQmkeO/Xopj1xFBREFr8ZZjuRhA++PEB+t05ggwBucpbH8i/n5C1ZU0EEEmRZnSMxoIYcarKigA0Cb1zpHAyZnGj21xqICAA9dcvo4UgEdZ41FBZSTzEOn30f6QeE3Vhl0gLN+2RGDzZPMHLHKoAO3MFy+ix4sDxFlvMXfrdNgFezy7qrXPaaJg0u27j5nneKrCjJ4pf4e3m4DVMcjNNNKxWnpo6jtnfnkunExB4GbuGKk5FNanpB1nJCjCsThJPAAJ8lVdSF5sSrklM2ZqmYdiC40G7Dfnhp57ZsQz6c3hylEO6ZoZQJxqiVgbhoQK3T6AIgU4rbjxthAPF6NAwAOAcS+ixlp/WBFJRDi0fj2RtcjWRwif8Qdu/w3EKLcu3/YslnrZzwo24UQQvwFCrp/iM1NnHwAAAAASUVORK5CYII='
_tray_icon_notallowed = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAPcICPcLC/cMDPcQEPcSEvcXF/cYGPcaGvcbG/ccHPgxMfgyMvg0NPg5Ofg6Ovg7O/hBQfhCQvlFRflGRvljY/pkZPplZfpnZ/p2dgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMgEwNYAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABE0lEQVQ4T4WT65bDIAiExWbbtN0m3Uua+P4P6g4jGtN4NvNL4DuCCC5WWobe++uwmEmtwNxJUTebcwWCt5jJBwsYcKf3NE4hTOOJxj1FEnBTz4NH6qH2jUcCGr/QLLpkQgHe/6VWJXVqFgBB4yI/KVCkBCoFgPrPHw0CWbwCL8RibBFwzQDQH62/QeAtHQBeADUIDbkF/UnmnkB1ixtERrN3xCgyuF5kMntHTCJXh2vyv+wIdMhvgTeCQJ0C2hBMgSKfZlM1wSLXZ5oqgs8sjSpaCQ2VVlfKhLU6fdZGSvyWz9JMb+NE4jt/Nwfm0yJZSkBpYDg7TcJGrjm0Z7jK0B6P/fHiHK8e9Pp/eSmuf1+vf4x/ralnCN9IrncAAAAASUVORK5CYII='
_tray_icon_stop = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAANsAANsBAdsCAtwEBNwFBdwHB9wICNwLC90MDN0NDd0PD90REd0SEt4TE94UFN4WFt4XF94ZGeAjI+AlJeEnJ+EpKeEqKuErK+EsLOEuLuIvL+IyMuIzM+M1NeM2NuM3N+M6OuM8POQ9PeQ+PuQ/P+RAQOVISOVJSeVKSuZLS+ZOTuZQUOZRUedSUudVVehbW+lhYeljY+poaOtvb+twcOtxcetzc+t0dOx3d+x4eOx6eu19fe1+fu2AgO2Cgu6EhO6Ghu6Hh+6IiO6Jie+Kiu+Li++MjO+Nje+Oju+QkPCUlPCVlfKgoPKkpPKlpfKmpvOrq/SurvSxsfSysvW4uPW6uvW7u/W8vPa9vfa+vvbAwPbCwvfExPfFxffGxvfHx/fIyPfJyffKyvjLy/jNzfjQ0PjR0fnS0vnU1PnY2Pvg4Pvi4vvj4/vl5fvm5vzo6Pzr6/3u7v3v7/3x8f3z8/309P719f729v739/74+P75+f76+v77+//8/P/9/f/+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPHCyoUAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABnUlEQVQ4T33S50PTQBgG8D6lzLbsIUv2kD0FFWTvPWTvISDIUBGV1ecvj+8luZTR9P1wSe755XK5O4+hK4gn5bc7DcMBz/InQoMXeVjY4FXuCAtEyLUwQcTcFgq45JYQ4JqbwhMtV8IjeUJDjQ+5paqCyG9srEsGgoUlpeXpIjxA1nfyi2+Jqmo7Q9JeV+ODerpvBQTM8/ySzQ3t+xxoL7h7nJve5jd85M7wJq9McHaT8o6TwBTfIIfHQGzoAZ/YiSTSq8D5dSDQVqFADrJ5KFMLPaKLHQiQMQoscClezdgCB4CXD/jM90izR8g85UaKA3YAn4AejhV189acA5LX+DVOg00gnvfoVX/BRQsgbplNGqzLusgIffx1tDchiyRgdRbVHNdgRRZHQD9H1asm+PMzYyYMtoBU/sYgRxxgrmGtBRL/cnf5RL4zzCEHZF2QE14LoOWf6B9vMcJBG/iBxKo8dVtYnyStv6yuUq7FLfmqTzbLEOFest1GNGEemCjCPnKuwjm0LsLMbRBJWLkGr4WdO+Cl0HkYPBc6N4z//HcQqVwcOuIAAAAASUVORK5CYII='
_tray_icon_exclamation = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAN0zM900NN01Nd02Nt03N944ON45Od46Ot47O98/P99BQd9CQt9DQ+FPT+JSUuJTU+JUVOJVVeJWVuNbW+ReXuVjY+Zra+dxceh4eOl7e+l8fOl+ful/f+qBgeqCguqDg+qFheuJieuLi+yPj+yQkO2Wlu+cnO+hofGqqvGtrfre3vrf3/ri4vvn5/75+f76+v/+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQ8SQkAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABJElEQVQ4T4WS63KCMBBGsyBai62X0otY0aq90ZZa3v/dtpvsJwTijOfXt7tnILOJYY9tNonjNCtQOlqhuKKG0RrNVjgkmIHBHgMId+h7zHSiwg2a9FNVVYScupETmjkd67o+CWpYwft+R6CpCgeUlq5AOyf45+8JsRUKFI6eQLkI3n5CIREBUekLxGaLpATCymRISiAszARJCYSxiZGUQKDLQoqgnPnFhUPOTWeRoZD3FvVZlmVHkE2OEM9iV71GVoZDBGUpAg9QWN5/jx+Ilsi9hz0q4VHOWD+hEF70yc1QEr1a4Q0F0S3eJDfLuv8T4QEFXduZE1rj+et7g6hzCDxF08N+X4DAu+6lUSTnc5wE5tx73ckSTV8QVoux3N88Rykw/wP3i+vwPKk17AAAAABJRU5ErkJggg=='
_tray_icon_none = None
SYSTEM_TRAY_MESSAGE_ICON_INFORMATION = _tray_icon_success
SYSTEM_TRAY_MESSAGE_ICON_WARNING = _tray_icon_exclamation
SYSTEM_TRAY_MESSAGE_ICON_CRITICAL = _tray_icon_stop
SYSTEM_TRAY_MESSAGE_ICON_NOICON = _tray_icon_none


RELIEF_RAISED = 'raised'
RELIEF_SUNKEN = 'sunken'
RELIEF_FLAT = 'flat'
RELIEF_RIDGE = 'ridge'
RELIEF_GROOVE = 'groove'
RELIEF_SOLID = 'solid'
RELIEF_LIST = (RELIEF_RAISED, RELIEF_FLAT, RELIEF_SUNKEN, RELIEF_RIDGE, RELIEF_SOLID, RELIEF_GROOVE)

# These are the spepific themes that tkinter offers
THEME_DEFAULT = 'default'  # this is a TTK theme, not a PSG theme!!!
THEME_WINNATIVE = 'winnative'
THEME_CLAM = 'clam'
THEME_ALT = 'alt'
THEME_CLASSIC = 'classic'
THEME_VISTA = 'vista'
THEME_XPNATIVE = 'xpnative'

# The theme to use by default for all windows
DEFAULT_TTK_THEME = THEME_DEFAULT
ttk_theme_in_use = None
# TTK_THEME_LIST = ('default', 'winnative', 'clam', 'alt', 'classic', 'vista', 'xpnative')


USE_TTK_BUTTONS = None

DEFAULT_PROGRESS_BAR_COLOR = ('#01826B', '#D0D0D0')  # a nice green progress bar
DEFAULT_PROGRESS_BAR_COMPUTE = (
    '#000000',
    '#000000',
)  # Means that the progress bar colors should be computed from other colors
DEFAULT_PROGRESS_BAR_COLOR_OFFICIAL = ('#01826B', '#D0D0D0')  # a nice green progress bar
DEFAULT_PROGRESS_BAR_SIZE = (20, 20)  # Size of Progress Bar (characters for length, pixels for width)
DEFAULT_PROGRESS_BAR_BORDER_WIDTH = 1
DEFAULT_PROGRESS_BAR_RELIEF = RELIEF_GROOVE
# PROGRESS_BAR_STYLES = ('default', 'winnative', 'clam', 'alt', 'classic', 'vista', 'xpnative')
DEFAULT_PROGRESS_BAR_STYLE = DEFAULT_TTK_THEME
DEFAULT_METER_ORIENTATION = 'Horizontal'
DEFAULT_SLIDER_ORIENTATION = 'vertical'
DEFAULT_SLIDER_BORDER_WIDTH = 1
DEFAULT_SLIDER_RELIEF = tk.FLAT
DEFAULT_FRAME_RELIEF = tk.GROOVE

DEFAULT_LISTBOX_SELECT_MODE = tk.SINGLE
SELECT_MODE_MULTIPLE = tk.MULTIPLE
LISTBOX_SELECT_MODE_MULTIPLE = 'multiple'
SELECT_MODE_BROWSE = tk.BROWSE
LISTBOX_SELECT_MODE_BROWSE = 'browse'
SELECT_MODE_EXTENDED = tk.EXTENDED
LISTBOX_SELECT_MODE_EXTENDED = 'extended'
SELECT_MODE_SINGLE = tk.SINGLE
LISTBOX_SELECT_MODE_SINGLE = 'single'

TABLE_SELECT_MODE_NONE = tk.NONE
TABLE_SELECT_MODE_BROWSE = tk.BROWSE
TABLE_SELECT_MODE_EXTENDED = tk.EXTENDED
DEFAULT_TABLE_SELECT_MODE = TABLE_SELECT_MODE_EXTENDED
TABLE_CLICKED_INDICATOR = '+CLICKED+'  # Part of the tuple returned as an event when a Table element has click events enabled
DEFAULT_MODAL_WINDOWS_ENABLED = True
DEFAULT_MODAL_WINDOWS_FORCED = False

TAB_LOCATION_TOP = 'top'
TAB_LOCATION_TOP_LEFT = 'topleft'
TAB_LOCATION_TOP_RIGHT = 'topright'
TAB_LOCATION_LEFT = 'left'
TAB_LOCATION_LEFT_TOP = 'lefttop'
TAB_LOCATION_LEFT_BOTTOM = 'leftbottom'
TAB_LOCATION_RIGHT = 'right'
TAB_LOCATION_RIGHT_TOP = 'righttop'
TAB_LOCATION_RIGHT_BOTTOM = 'rightbottom'
TAB_LOCATION_BOTTOM = 'bottom'
TAB_LOCATION_BOTTOM_LEFT = 'bottomleft'
TAB_LOCATION_BOTTOM_RIGHT = 'bottomright'


TITLE_LOCATION_TOP = tk.N
TITLE_LOCATION_BOTTOM = tk.S
TITLE_LOCATION_LEFT = tk.W
TITLE_LOCATION_RIGHT = tk.E
TITLE_LOCATION_TOP_LEFT = tk.NW
TITLE_LOCATION_TOP_RIGHT = tk.NE
TITLE_LOCATION_BOTTOM_LEFT = tk.SW
TITLE_LOCATION_BOTTOM_RIGHT = tk.SE

TEXT_LOCATION_TOP = tk.N
TEXT_LOCATION_BOTTOM = tk.S
TEXT_LOCATION_LEFT = tk.W
TEXT_LOCATION_RIGHT = tk.E
TEXT_LOCATION_TOP_LEFT = tk.NW
TEXT_LOCATION_TOP_RIGHT = tk.NE
TEXT_LOCATION_BOTTOM_LEFT = tk.SW
TEXT_LOCATION_BOTTOM_RIGHT = tk.SE
TEXT_LOCATION_CENTER = tk.CENTER

GRAB_ANYWHERE_IGNORE_THESE_WIDGETS = (
    ttk.Sizegrip,
    tk.Scale,
    ttk.Scrollbar,
    tk.Scrollbar,
    tk.Entry,
    tk.Text,
    tk.PanedWindow,
    tk.Listbox,
    tk.OptionMenu,
    ttk.Treeview,
)

# These timer routines are to help you quickly time portions of code.  Place the timer_start call at the point
# you want to start timing and the timer_stop at the end point. The delta between the start and stop calls
# is returned from calling timer_stop


EMOJI_BASE64_FACEPALM = b' '
EMOJI_BASE64_FRUSTRATED = b' '
EMOJI_BASE64_NOTUNDERSTANDING = b' '
EMOJI_BASE64_PONDER = b' '
EMOJI_BASE64_SAD = b' '
EMOJI_BASE64_SKEPTICAL = b' '
EMOJI_BASE64_THINK = b' '
EMOJI_BASE64_DREAMING = b' '
EMOJI_BASE64_WEARY = b' '
EMOJI_BASE64_YIKES = b' '

EMOJI_BASE64_HAPPY_GASP = b' '

EMOJI_BASE64_HAPPY_IDEA = b' '

EMOJI_BASE64_HAPPY_JOY = b' '

EMOJI_BASE64_HAPPY_LAUGH = b' '

EMOJI_BASE64_HAPPY_STARE = b' '

ICON_BUY_ME_A_COFFEE = b' '

EMOJI_BASE64_HAPPY_RELIEF = b' '

EMOJI_BASE64_HAPPY_BIG_SMILE = b' '
EMOJI_BASE64_HAPPY_CONTENT = b' '

EMOJI_BASE64_HAPPY_HEARTS = b' '

EMOJI_BASE64_HAPPY_THUMBS_UP = b' '

EMOJI_BASE64_HAPPY_WINK = b' '


EMOJI_BASE64_CRY = b' '

EMOJI_BASE64_DEAD = b' '

EMOJI_BASE64_FINGERS_CROSSED = b' '

EMOJI_BASE64_GUESS = b' '

EMOJI_BASE64_CLAP = b' '

EMOJI_BASE64_NO_HEAR = b' '

EMOJI_BASE64_NO_SEE = b' '

EMOJI_BASE64_NO_SPEAK = b' '

EMOJI_BASE64_PRAY = b' '

EMOJI_BASE64_ZIPPED_SHUT = b' '

EMOJI_BASE64_NO_HEAR = b' '

EMOJI_BASE64_MIKE = b' '

EMOJI_BASE64_SUPERHERO = b' '

EMOJI_BASE64_BLANK_STARE = b' '

EMOJI_BASE64_COOL = b' '

EMOJI_BASE64_EYE_ROLL = b' '

EMOJI_BASE64_OK = b' '

EMOJI_BASE64_QUESTION = b' '

EMOJI_BASE64_SKEPTIC = b' '

EMOJI_BASE64_SLEEPING = b' '

EMOJI_BASE64_TEAR = b' '

EMOJI_BASE64_UPSIDE_DOWN = b' '

EMOJI_BASE64_WIZARD = b' '

EMOJI_BASE64_CRAZY = b' '

EMOJI_BASE64_GLASSES = b' '

EMOJI_BASE64_HEAD_EXPLODE = b' '


EMOJI_BASE64_LAPTOP = b' '


EMOJI_BASE64_PARTY = b' '

EMOJI_BASE64_RAINEDON = b' '

EMOJI_BASE64_READING = b' '

EMOJI_BASE64_SANTA = b' '

EMOJI_BASE64_SEARCH = b' '

EMOJI_BASE64_WAVE = b' '

EMOJI_BASE64_DEPRESSED = b' '


EMOJI_BASE64_GOLD_STAR = b' '

EMOJI_BASE64_HONEST = b' '

EMOJI_BASE64_ILL = b' '

EMOJI_BASE64_ILL2 = b' '

EMOJI_BASE64_KEY = b' '

EMOJI_BASE64_MASK = b' '

EMOJI_BASE64_SALUTE = b' '

EMOJI_BASE64_SCREAM = b' '

EMOJI_BASE64_SMIRKING = b' '

EMOJI_BASE64_WARNING2 = b' '

EMOJI_BASE64_WARNING = b' '

EMOJI_BASE64_JEDI = b' '


EMOJI_BASE64_HAPPY_LIST = [
    EMOJI_BASE64_HAPPY_STARE,
    EMOJI_BASE64_BLANK_STARE,
    EMOJI_BASE64_HAPPY_LAUGH,
    EMOJI_BASE64_HAPPY_JOY,
    EMOJI_BASE64_HAPPY_IDEA,
    EMOJI_BASE64_HAPPY_GASP,
    EMOJI_BASE64_HAPPY_RELIEF,
    EMOJI_BASE64_HAPPY_WINK,
    EMOJI_BASE64_HAPPY_THUMBS_UP,
    EMOJI_BASE64_HAPPY_HEARTS,
    EMOJI_BASE64_HAPPY_CONTENT,
    EMOJI_BASE64_HAPPY_BIG_SMILE,
    EMOJI_BASE64_PRAY,
    EMOJI_BASE64_GUESS,
    EMOJI_BASE64_FINGERS_CROSSED,
    EMOJI_BASE64_CLAP,
    EMOJI_BASE64_COOL,
    EMOJI_BASE64_UPSIDE_DOWN,
    EMOJI_BASE64_OK,
    EMOJI_BASE64_COOL,
    EMOJI_BASE64_GLASSES,
    EMOJI_BASE64_HEAD_EXPLODE,
    EMOJI_BASE64_GLASSES,
    EMOJI_BASE64_LAPTOP,
    EMOJI_BASE64_PARTY,
    EMOJI_BASE64_READING,
    EMOJI_BASE64_SANTA,
    EMOJI_BASE64_SEARCH,
    EMOJI_BASE64_WAVE,
    EMOJI_BASE64_KEY,
    EMOJI_BASE64_SALUTE,
    EMOJI_BASE64_HONEST,
    EMOJI_BASE64_WIZARD,
    EMOJI_BASE64_JEDI,
    EMOJI_BASE64_GOLD_STAR,
    EMOJI_BASE64_SMIRKING,
]

EMOJI_BASE64_SAD_LIST = [
    EMOJI_BASE64_YIKES,
    EMOJI_BASE64_WEARY,
    EMOJI_BASE64_DREAMING,
    EMOJI_BASE64_SLEEPING,
    EMOJI_BASE64_THINK,
    EMOJI_BASE64_SKEPTIC,
    EMOJI_BASE64_SKEPTICAL,
    EMOJI_BASE64_FACEPALM,
    EMOJI_BASE64_FRUSTRATED,
    EMOJI_BASE64_PONDER,
    EMOJI_BASE64_NOTUNDERSTANDING,
    EMOJI_BASE64_QUESTION,
    EMOJI_BASE64_CRY,
    EMOJI_BASE64_TEAR,
    EMOJI_BASE64_DEAD,
    EMOJI_BASE64_ZIPPED_SHUT,
    EMOJI_BASE64_NO_HEAR,
    EMOJI_BASE64_NO_SEE,
    EMOJI_BASE64_NO_SPEAK,
    EMOJI_BASE64_EYE_ROLL,
    EMOJI_BASE64_CRAZY,
    EMOJI_BASE64_RAINEDON,
    EMOJI_BASE64_DEPRESSED,
    EMOJI_BASE64_ILL,
    EMOJI_BASE64_ILL2,
    EMOJI_BASE64_MASK,
    EMOJI_BASE64_WARNING,
    EMOJI_BASE64_WARNING2,
    EMOJI_BASE64_SCREAM,
]
EMOJI_BASE64_LIST = EMOJI_BASE64_HAPPY_LIST + EMOJI_BASE64_SAD_LIST

EMOJI_BASE64_JASON = EMOJI_BASE64_WIZARD
EMOJI_BASE64_TANAY = EMOJI_BASE64_JEDI


def _random_error_emoji():
    c = random.choice(EMOJI_BASE64_SAD_LIST)
    return c


def _random_happy_emoji():
    c = random.choice(EMOJI_BASE64_HAPPY_LIST)
    return c


def timer_start():
    """
    Time your code easily.... starts the timer.
    Uses the time.time value, a technique known to not be terribly accurage, but tis' gclose enough for our purposes
    """
    global g_time_start
    warnings.warn('The timer_start function is deprecated and will be removed in a future version', DeprecationWarning, stacklevel=2)

    g_time_start = time.time()


def timer_stop():
    """
    Time your code easily.... stop the timer and print the number of MILLISECONDS since the timer start

    :return: delta in MILLISECONDS from timer_start was called
    :rtype:  int
    """
    global g_time_delta, g_time_end
    warnings.warn('The timer_stop function is deprecated and will be removed in a future version', DeprecationWarning, stacklevel=2)

    g_time_end = time.time()
    g_time_delta = g_time_end - g_time_start
    return int(g_time_delta * 1000)


def timer_stop_usec():
    """
    Time your code easily.... stop the timer and print the number of MICROSECONDS since the timer start

    :return: delta in MICROSECONDS from timer_start was called
    :rtype:  int
    """
    global g_time_delta, g_time_end
    warnings.warn('The timer_stop_usec function is deprecated and will be removed in a future version', DeprecationWarning, stacklevel=2)

    g_time_end = time.time()
    g_time_delta = g_time_end - g_time_start
    return int(g_time_delta * 1000000)


def _timeit(func):
    """
    Put @_timeit as a decorator to a function to get the time spent in that function printed out

    :param func: Decorated function
    :type func:
    :return:     Execution time for the decorated function
    :rtype:
    """
    warnings.warn('The _timeit function is deprecated and will be removed in a future version', DeprecationWarning, stacklevel=2)

    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} executed in {end - start:.4f} seconds')
        return result

    return wrapper


_timeit_counter = 0
MAX_TIMEIT_COUNT = 1000
_timeit_total = 0


def _timeit_summary(func):
    """
    Same as the timeit decorator except that the value is shown as an averave
    Put @_timeit_summary as a decorator to a function to get the time spent in that function printed out

    :param func: Decorated function
    :type func:
    :return:     Execution time for the decorated function
    :rtype:
    """
    warnings.warn('The _timeit_summary function is deprecated and will be removed in a future version', DeprecationWarning, stacklevel=2)

    @wraps(func)
    def wrapper(*args, **kwargs):
        global _timeit_counter, _timeit_total

        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        _timeit_counter += 1
        _timeit_total += end - start
        if _timeit_counter > MAX_TIMEIT_COUNT:
            print(f'{func.__name__} executed in {_timeit_total / MAX_TIMEIT_COUNT:.4f} seconds')
            _timeit_counter = 0
            _timeit_total = 0
        return result

    return wrapper


def formatted_datetime_now():
    """
    Returns a string with current date and time formatted YYYY-MM-DD HH:MM:SS for easy logging

    :return:    String with date and time formatted YYYY-MM-DD  HH:MM:SS
    :rtype:     (str)
    """
    now = datetime.datetime.now()
    current_time = now.strftime('%Y-%m-%d %H:%M:%S')
    return current_time


def running_linux():
    """
    Determines the OS is Linux by using sys.platform

    Returns True if Linux

    :return: True if sys.platform indicates running Linux
    :rtype:  (bool)
    """
    return sys.platform.startswith('linux')


def running_mac():
    """
    Determines the OS is Mac by using sys.platform

    Returns True if Mac

    :return: True if sys.platform indicates running Mac
    :rtype:  (bool)
    """
    return sys.platform.startswith('darwin')


def running_windows():
    """
    Determines the OS is Windows by using sys.platform

    Returns True if Windows

    :return: True if sys.platform indicates running Windows
    :rtype:  (bool)
    """
    return sys.platform.startswith('win')


def running_trinket():
    """
    A special case for Trinket.  Checks both the OS and the number of environment variables
    Currently, Trinket only has ONE environment variable.  This fact is used to figure out if Trinket is being used.

    Returns True if "Trinket" (in theory)

    :return: True if sys.platform indicates Linux and the number of environment variables is 1
    :rtype:  (bool)
    """
    if sys.platform.startswith('linux') and socket.gethostname().startswith('pygame-'):
        return True
    return False


def running_replit():
    """
    A special case for REPLIT.  Checks both the OS and for the existance of the number of environment variable REPL_OWNER
    Currently, Trinket only has ONE environment variable.  This fact is used to figure out if Trinket is being used.

    Returns True if running on "replit"

    :return: True if sys.platform indicates Linux and setting REPL_OWNER is found in the environment variables
    :rtype:  (bool)
    """
    if 'REPL_OWNER' in os.environ and sys.platform.startswith('linux'):
        return True
    return False


# ----====----====----==== Constants the user should NOT change ====----====----====----#
ThisRow = 555666777  # magic number

# DEFAULT_WINDOW_ICON = ''
MESSAGE_BOX_LINE_WIDTH = 60

# "Special" Key Values.. reserved
# Key representing a Read timeout
EVENT_TIMEOUT = TIMEOUT_EVENT = TIMEOUT_KEY = '__TIMEOUT__'
EVENT_TIMER = TIMER_KEY = '__TIMER EVENT__'
WIN_CLOSED = WINDOW_CLOSED = None
WINDOW_CLOSE_ATTEMPTED_EVENT = WIN_X_EVENT = WIN_CLOSE_ATTEMPTED_EVENT = '-WINDOW CLOSE ATTEMPTED-'
WINDOW_CONFIG_EVENT = '__WINDOW CONFIG__'
TITLEBAR_MINIMIZE_KEY = '__TITLEBAR MINIMIZE__'
TITLEBAR_MAXIMIZE_KEY = '__TITLEBAR MAXIMIZE__'
TITLEBAR_CLOSE_KEY = '__TITLEBAR CLOSE__'
TITLEBAR_IMAGE_KEY = '__TITLEBAR IMAGE__'
TITLEBAR_TEXT_KEY = '__TITLEBAR TEXT__'
TITLEBAR_DO_NOT_USE_AN_ICON = '__TITLEBAR_NO_ICON__'

# Key indicating should not create any return values for element
WRITE_ONLY_KEY = '__WRITE ONLY__'

MENU_DISABLED_CHARACTER = '!'
MENU_SHORTCUT_CHARACTER = '&'
MENU_KEY_SEPARATOR = '::'
MENU_SEPARATOR_LINE = '---'
MENU_RIGHT_CLICK_EDITME_EXIT = ['', ['Edit Me', 'Exit']]
MENU_RIGHT_CLICK_EDITME_VER_EXIT = ['', ['Edit Me', 'Version', 'Exit']]
MENU_RIGHT_CLICK_EDITME_VER_LOC_EXIT = ['', ['Edit Me', 'Version', 'File Location', 'Exit']]
MENU_RIGHT_CLICK_EDITME_VER_SETTINGS_EXIT = ['', ['Edit Me', 'Settings', 'Version', 'Exit']]
MENU_RIGHT_CLICK_EXIT = ['', ['Exit']]
MENU_RIGHT_CLICK_DISABLED = ['', []]
_MENU_RIGHT_CLICK_TABGROUP_DEFAULT = ['TABGROUP DEFAULT', []]
ENABLE_TK_WINDOWS = False

USE_CUSTOM_TITLEBAR = None
CUSTOM_TITLEBAR_BACKGROUND_COLOR = None
CUSTOM_TITLEBAR_TEXT_COLOR = None
CUSTOM_TITLEBAR_ICON = None
CUSTOM_TITLEBAR_FONT = None
TITLEBAR_METADATA_MARKER = 'This window has a titlebar'

CUSTOM_MENUBAR_METADATA_MARKER = 'This is a custom menubar'

SUPPRESS_ERROR_POPUPS = False
SUPPRESS_RAISE_KEY_ERRORS = True
SUPPRESS_KEY_GUESSING = False
SUPPRESS_WIDGET_NOT_FINALIZED_WARNINGS = False
ENABLE_TREEVIEW_869_PATCH = True

# These are now set based on the global settings file
ENABLE_MAC_NOTITLEBAR_PATCH = False
ENABLE_MAC_MODAL_DISABLE_PATCH = False
ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR = True
ENABLE_MAC_ALPHA_99_PATCH = False

OLD_TABLE_TREE_SELECTED_ROW_COLORS = ('#FFFFFF', '#4A6984')
ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS = ('SystemHighlightText', 'SystemHighlight')

# Some handy unicode symbols
SYMBOL_SQUARE = '█'
SYMBOL_CIRCLE = '⚫'
SYMBOL_CIRCLE_OUTLINE = '◯'
SYMBOL_BULLET = '•'
SYMBOL_UP = '▲'
SYMBOL_RIGHT = '►'
SYMBOL_LEFT = '◄'
SYMBOL_DOWN = '▼'
SYMBOL_X = '❎'
SYMBOL_CHECK = '✅'
SYMBOL_CHECK_SMALL = '✓'
SYMBOL_X_SMALL = '✗'
SYMBOL_BALLOT_X = '☒'
SYMBOL_BALLOT_CHECK = '☑'
SYMBOL_LEFT_DOUBLE = '«'
SYMBOL_RIGHT_DOUBLE = '»'
SYMBOL_LEFT_ARROWHEAD = '⮜'
SYMBOL_RIGHT_ARROWHEAD = '⮞'
SYMBOL_UP_ARROWHEAD = '⮝'
SYMBOL_DOWN_ARROWHEAD = '⮟'

if sum([int(i) for i in tclversion_detailed.split('.')]) > 19:
    SYMBOL_TITLEBAR_MINIMIZE = '_'
    SYMBOL_TITLEBAR_MAXIMIZE = '◻'
    SYMBOL_TITLEBAR_CLOSE = 'Ｘ'
else:
    SYMBOL_TITLEBAR_MINIMIZE = '_'
    SYMBOL_TITLEBAR_MAXIMIZE = 'O'
    SYMBOL_TITLEBAR_CLOSE = 'X'

#################### PATHS for user_settings APIs ####################
# These paths are passed to os.path.expanduser to get the default path for user_settings
# They can be changed using set_options

DEFAULT_USER_SETTINGS_WIN_PATH = r'~\AppData\Local\PySimpleGUI\settings'
DEFAULT_USER_SETTINGS_LINUX_PATH = r'~/.config/PySimpleGUI/settings'
DEFAULT_USER_SETTINGS_MAC_PATH = r'~/Library/Application Support/PySimpleGUI/settings'
DEFAULT_USER_SETTINGS_TRINKET_PATH = r'.'
DEFAULT_USER_SETTINGS_REPLIT_PATH = r'.'
DEFAULT_USER_SETTINGS_UNKNOWN_OS_PATH = r'~/Library/Application Support/PySimpleGUI/settings'
DEFAULT_USER_SETTINGS_PATH = None  # value set by user to override all paths above
DEFAULT_USER_SETTINGS_PYSIMPLEGUI_PATH = None  # location of the global PySimpleGUI settings
DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME = '_PySimpleGUI_settings_global_.json'  # location of the global PySimpleGUI settings


# ====================================================================== #
# One-liner functions that are handy as f_ck                             #
# ====================================================================== #
def rgb(red, green, blue):
    """
    Given integer values of Red, Green, Blue, return a color string "#RRGGBB"
    :param red:   Red portion from 0 to 255
    :type red:    (int)
    :param green: Green portion from 0 to 255
    :type green:  (int)
    :param blue:  Blue portion from 0 to 255
    :type  blue:  (int)
    :return:      A single RGB String in the format "#RRGGBB" where each pair is a hex number.
    :rtype:       (str)
    """
    red = min(int(red), 255) if red > 0 else 0
    blue = min(int(blue), 255) if blue > 0 else 0
    green = min(int(green), 255) if green > 0 else 0
    return '#{:02x}{:02x}{:02x}'.format(red, green, blue)


# ====================================================================== #
# Enums for types                                                        #
# ====================================================================== #
# -------------------------  Button types  ------------------------- #
BUTTON_TYPE_BROWSE_FOLDER = 1
BUTTON_TYPE_BROWSE_FILE = 2
BUTTON_TYPE_BROWSE_FILES = 21
BUTTON_TYPE_SAVEAS_FILE = 3
BUTTON_TYPE_CLOSES_WIN = 5
BUTTON_TYPE_CLOSES_WIN_ONLY = 6
BUTTON_TYPE_READ_FORM = 7
BUTTON_TYPE_REALTIME = 9
BUTTON_TYPE_CALENDAR_CHOOSER = 30
BUTTON_TYPE_COLOR_CHOOSER = 40
BUTTON_TYPE_SHOW_DEBUGGER = 50

BROWSE_FILES_DELIMITER = ';'  # the delimiter to be used between each file in the returned string

FILE_TYPES_ALL_FILES = (('ALL Files', '*.* *'),)

BUTTON_DISABLED_MEANS_IGNORE = 'ignore'

# -------------------------  Element types  ------------------------- #

ELEM_TYPE_TEXT = 'text'
ELEM_TYPE_INPUT_TEXT = 'input'
ELEM_TYPE_INPUT_COMBO = 'combo'
ELEM_TYPE_INPUT_OPTION_MENU = 'option menu'
ELEM_TYPE_INPUT_RADIO = 'radio'
ELEM_TYPE_INPUT_MULTILINE = 'multiline'
ELEM_TYPE_INPUT_CHECKBOX = 'checkbox'
ELEM_TYPE_INPUT_SPIN = 'spind'
ELEM_TYPE_BUTTON = 'button'
ELEM_TYPE_IMAGE = 'image'
ELEM_TYPE_CANVAS = 'canvas'
ELEM_TYPE_FRAME = 'frame'
ELEM_TYPE_GRAPH = 'graph'
ELEM_TYPE_TAB = 'tab'
ELEM_TYPE_TAB_GROUP = 'tabgroup'
ELEM_TYPE_INPUT_SLIDER = 'slider'
ELEM_TYPE_INPUT_LISTBOX = 'listbox'
ELEM_TYPE_OUTPUT = 'output'
ELEM_TYPE_COLUMN = 'column'
ELEM_TYPE_MENUBAR = 'menubar'
ELEM_TYPE_PROGRESS_BAR = 'progressbar'
ELEM_TYPE_BLANK = 'blank'
ELEM_TYPE_TABLE = 'table'
ELEM_TYPE_TREE = 'tree'
ELEM_TYPE_ERROR = 'error'
ELEM_TYPE_SEPARATOR = 'separator'
ELEM_TYPE_STATUSBAR = 'statusbar'
ELEM_TYPE_PANE = 'pane'
ELEM_TYPE_BUTTONMENU = 'buttonmenu'
ELEM_TYPE_TITLEBAR = 'titlebar'
ELEM_TYPE_SIZEGRIP = 'sizegrip'

# STRETCH == ERROR ELEMENT as a filler

# -------------------------  Popup Buttons Types  ------------------------- #
POPUP_BUTTONS_YES_NO = 1
POPUP_BUTTONS_CANCELLED = 2
POPUP_BUTTONS_ERROR = 3
POPUP_BUTTONS_OK_CANCEL = 4
POPUP_BUTTONS_OK = 0
POPUP_BUTTONS_NO_BUTTONS = 5


PSG_THEME_PART_BUTTON_TEXT = 'Button Text Color'
PSG_THEME_PART_BUTTON_BACKGROUND = 'Button Background Color'
PSG_THEME_PART_BACKGROUND = 'Background Color'
PSG_THEME_PART_INPUT_BACKGROUND = 'Input Element Background Color'
PSG_THEME_PART_INPUT_TEXT = 'Input Element Text Color'
PSG_THEME_PART_TEXT = 'Text Color'
PSG_THEME_PART_SLIDER = 'Slider Color'
PSG_THEME_PART_LIST = [
    PSG_THEME_PART_BACKGROUND,
    PSG_THEME_PART_BUTTON_BACKGROUND,
    PSG_THEME_PART_BUTTON_TEXT,
    PSG_THEME_PART_INPUT_BACKGROUND,
    PSG_THEME_PART_INPUT_TEXT,
    PSG_THEME_PART_TEXT,
    PSG_THEME_PART_SLIDER,
]

# theme_button

TTK_SCROLLBAR_PART_TROUGH_COLOR = 'Trough Color'
TTK_SCROLLBAR_PART_BACKGROUND_COLOR = 'Background Color'
TTK_SCROLLBAR_PART_ARROW_BUTTON_ARROW_COLOR = 'Arrow Button Arrow Color'
TTK_SCROLLBAR_PART_FRAME_COLOR = 'Frame Color'
TTK_SCROLLBAR_PART_SCROLL_WIDTH = 'Frame Width'
TTK_SCROLLBAR_PART_ARROW_WIDTH = 'Arrow Width'
TTK_SCROLLBAR_PART_RELIEF = 'Relief'
TTK_SCROLLBAR_PART_LIST = [
    TTK_SCROLLBAR_PART_TROUGH_COLOR,
    TTK_SCROLLBAR_PART_BACKGROUND_COLOR,
    TTK_SCROLLBAR_PART_ARROW_BUTTON_ARROW_COLOR,
    TTK_SCROLLBAR_PART_FRAME_COLOR,
    TTK_SCROLLBAR_PART_SCROLL_WIDTH,
    TTK_SCROLLBAR_PART_ARROW_WIDTH,
    TTK_SCROLLBAR_PART_RELIEF,
]
TTK_SCROLLBAR_PART_THEME_BASED_LIST = [
    TTK_SCROLLBAR_PART_TROUGH_COLOR,
    TTK_SCROLLBAR_PART_BACKGROUND_COLOR,
    TTK_SCROLLBAR_PART_ARROW_BUTTON_ARROW_COLOR,
    TTK_SCROLLBAR_PART_FRAME_COLOR,
]
DEFAULT_TTK_PART_MAPPING_DICT = {
    TTK_SCROLLBAR_PART_TROUGH_COLOR: PSG_THEME_PART_SLIDER,
    TTK_SCROLLBAR_PART_BACKGROUND_COLOR: PSG_THEME_PART_BUTTON_BACKGROUND,
    TTK_SCROLLBAR_PART_ARROW_BUTTON_ARROW_COLOR: PSG_THEME_PART_BUTTON_TEXT,
    TTK_SCROLLBAR_PART_FRAME_COLOR: PSG_THEME_PART_BACKGROUND,
    TTK_SCROLLBAR_PART_SCROLL_WIDTH: 12,
    TTK_SCROLLBAR_PART_ARROW_WIDTH: 12,
    TTK_SCROLLBAR_PART_RELIEF: RELIEF_RAISED,
}

ttk_part_mapping_dict = copy.copy(DEFAULT_TTK_PART_MAPPING_DICT)


class TTKPartOverrides:
    """
    This class contains "overrides" to the defaults for ttk scrollbars that are defined in the global settings file.
    This class is used in every element, in the Window class and there's a global one that is used by set_options.
    """

    def __init__(
        self,
        sbar_trough_color=None,
        sbar_background_color=None,
        sbar_arrow_color=None,
        sbar_width=None,
        sbar_arrow_width=None,
        sbar_frame_color=None,
        sbar_relief=None,
    ):
        self.sbar_trough_color = sbar_trough_color
        self.sbar_background_color = sbar_background_color
        self.sbar_arrow_color = sbar_arrow_color
        self.sbar_width = sbar_width
        self.sbar_arrow_width = sbar_arrow_width
        self.sbar_frame_color = sbar_frame_color
        self.sbar_relief = sbar_relief


ttk_part_overrides_from_options = TTKPartOverrides()


# -------------------------  tkinter BASIC cursors... there are some OS dependent ones too  ------------------------- #
# TKINTER_CURSORS = ['X_cursor', 'arrow', 'based_arrow_down', 'based_arrow_up', 'boat', 'bogosity', 'bottom_left_corner', 'bottom_right_corner', 'bottom_side', 'bottom_tee', 'box_spiral', 'center_ptr', 'circle', 'clock', 'coffee_mug', 'cross', 'cross_reverse', 'crosshair', 'diamond_cross', 'dot', 'dotbox', 'double_arrow', 'draft_large', 'draft_small', 'draped_box', 'exchange', 'fleur', 'gobbler', 'gumby', 'hand1', 'hand2', 'heart', 'icon', 'iron_cross', 'left_ptr', 'left_side', 'left_tee', 'leftbutton', 'll_angle', 'lr_angle', 'man', 'middlebutton', 'mouse', 'pencil', 'pirate', 'plus', 'question_arrow', 'right_ptr', 'right_side', 'right_tee', 'rightbutton', 'rtl_logo', 'sailboat', 'sb_down_arrow', 'sb_h_double_arrow', 'sb_left_arrow', 'sb_right_arrow', 'sb_up_arrow', 'sb_v_double_arrow', 'shuttle', 'sizing', 'spider', 'spraycan', 'star', 'target', 'tcross', 'top_left_arrow', 'top_left_corner', 'top_right_corner', 'top_side', 'top_tee', 'trek', 'ul_angle', 'umbrella', 'ur_angle', 'watch', 'xterm',]

# fmt: off
TKINTER_CURSORS = ['X_cursor', 'arrow', 'based_arrow_down', 'based_arrow_up', 'boat', 'bogosity', 'bottom_left_corner', 'bottom_right_corner', 'bottom_side', 'bottom_tee', 'box_spiral', 'center_ptr', 'circle', 'clock', 'coffee_mug', 'cross', 'cross_reverse', 'crosshair', 'diamond_cross', 'dot', 'dotbox', 'double_arrow', 'draft_large', 'draft_small', 'draped_box', 'exchange', 'fleur', 'gobbler', 'gumby', 'hand1', 'hand2', 'heart', 'ibeam', 'icon', 'iron_cross', 'left_ptr', 'left_side', 'left_tee', 'leftbutton', 'll_angle', 'lr_angle', 'man', 'middlebutton', 'mouse', 'no', 'none', 'pencil', 'pirate', 'plus', 'question_arrow', 'right_ptr', 'right_side', 'right_tee', 'rightbutton', 'rtl_logo', 'sailboat', 'sb_down_arrow', 'sb_h_double_arrow', 'sb_left_arrow', 'sb_right_arrow', 'sb_up_arrow', 'sb_v_double_arrow', 'shuttle', 'size', 'size_ne_sw', 'size_ns', 'size_nw_se', 'size_we', 'sizing', 'spider', 'spraycan', 'star', 'starting', 'target', 'tcross', 'top_left_arrow', 'top_left_corner', 'top_right_corner', 'top_side', 'top_tee', 'trek', 'ul_angle', 'umbrella', 'uparrow', 'ur_angle', 'wait', 'watch', 'xterm',]
# -------------------------  tkinter key codes for bindings  ------------------------- #
# The keycode that when pressed will take a snapshot of the current window
DEFAULT_WINDOW_SNAPSHOT_KEY_CODE = None
DEFAULT_WINDOW_SNAPSHOT_KEY = '--SCREENSHOT THIS WINDOW--'
tkinter_keysyms = ('space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'minus', 'period', 'slash', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'nobreakspace', 'exclamdown', 'cent', 'sterling', 'currency', 'yen', 'brokenbar', 'section', 'diaeresis', 'copyright', 'ordfeminine', 'guillemotleft', 'notsign', 'hyphen', 'registered', 'macron', 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute', 'mu', 'paragraph', 'periodcentered', 'cedilla', 'onesuperior', 'masculine', 'guillemotright', 'onequarter', 'onehalf', 'threequarters', 'questiondown', 'Agrave', 'Aacute', 'Acircumflex', 'Atilde', 'Adiaeresis', 'Aring', 'AE', 'Ccedilla', 'Egrave', 'Eacute', 'Ecircumflex', 'Ediaeresis', 'Igrave', 'Iacute', 'Icircumflex', 'Idiaeresis', 'Eth', 'Ntilde', 'Ograve', 'Oacute', 'Ocircumflex', 'Otilde', 'Odiaeresis', 'multiply', 'Ooblique', 'Ugrave', 'Uacute', 'Ucircumflex', 'Udiaeresis', 'Yacute', 'Thorn', 'ssharp', 'agrave', 'aacute', 'acircumflex', 'atilde', 'adiaeresis', 'aring', 'ae', 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'ediaeresis', 'igrave', 'iacute', 'icircumflex', 'idiaeresis', 'eth', 'ntilde', 'ograve', 'oacute', 'ocircumflex', 'otilde', 'odiaeresis', 'division', 'oslash', 'ugrave', 'uacute', 'ucircumflex', 'udiaeresis', 'yacute', 'thorn', 'ydiaeresis', 'Aogonek', 'breve', 'Lstroke', 'Lcaron', 'Sacute', 'Scaron', 'Scedilla', 'Tcaron', 'Zacute', 'Zcaron', 'Zabovedot', 'aogonek', 'ogonek', 'lstroke', 'lcaron', 'sacute', 'caron', 'scaron', 'scedilla', 'tcaron', 'zacute', 'doubleacute', 'zcaron', 'zabovedot', 'Racute', 'Abreve', 'Cacute', 'Ccaron', 'Eogonek', 'Ecaron', 'Dcaron', 'Nacute', 'Ncaron', 'Odoubleacute', 'Rcaron', 'Uring', 'Udoubleacute', 'Tcedilla', 'racute', 'abreve', 'cacute', 'ccaron', 'eogonek', 'ecaron', 'dcaron', 'nacute', 'ncaron', 'odoubleacute', 'rcaron', 'uring', 'udoubleacute', 'tcedilla', 'abovedot', 'Hstroke', 'Hcircumflex', 'Iabovedot', 'Gbreve', 'Jcircumflex', 'hstroke', 'hcircumflex', 'idotless', 'gbreve', 'jcircumflex', 'Cabovedot', 'Ccircumflex', 'Gabovedot', 'Gcircumflex', 'Ubreve', 'Scircumflex', 'cabovedot', 'ccircumflex', 'gabovedot', 'gcircumflex', 'ubreve', 'scircumflex', 'kappa', 'Rcedilla', 'Itilde', 'Lcedilla', 'Emacron', 'Gcedilla', 'Tslash', 'rcedilla', 'itilde', 'lcedilla', 'emacron', 'gacute', 'tslash', 'ENG', 'eng', 'Amacron', 'Iogonek', 'Eabovedot', 'Imacron', 'Ncedilla', 'Omacron', 'Kcedilla', 'Uogonek', 'Utilde', 'Umacron', 'amacron', 'iogonek', 'eabovedot', 'imacron', 'ncedilla', 'omacron', 'kcedilla', 'uogonek', 'utilde', 'umacron', 'overline', 'kana_fullstop', 'kana_openingbracket', 'kana_closingbracket', 'kana_comma', 'kana_middledot', 'kana_WO', 'kana_a', 'kana_i', 'kana_u', 'kana_e', 'kana_o', 'kana_ya', 'kana_yu', 'kana_yo', 'kana_tu', 'prolongedsound', 'kana_A', 'kana_I', 'kana_U', 'kana_E', 'kana_O', 'kana_KA', 'kana_KI', 'kana_KU', 'kana_KE', 'kana_KO', 'kana_SA', 'kana_SHI', 'kana_SU', 'kana_SE', 'kana_SO', 'kana_TA', 'kana_TI', 'kana_TU', 'kana_TE', 'kana_TO', 'kana_NA', 'kana_NI', 'kana_NU', 'kana_NE', 'kana_NO', 'kana_HA', 'kana_HI', 'kana_HU', 'kana_HE', 'kana_HO', 'kana_MA', 'kana_MI', 'kana_MU', 'kana_ME', 'kana_MO', 'kana_YA', 'kana_YU', 'kana_YO', 'kana_RA', 'kana_RI', 'kana_RU', 'kana_RE', 'kana_RO', 'kana_WA', 'kana_N', 'voicedsound', 'semivoicedsound', 'Arabic_comma', 'Arabic_semicolon', 'Arabic_question_mark', 'Arabic_hamza', 'Arabic_maddaonalef', 'Arabic_hamzaonalef', 'Arabic_hamzaonwaw', 'Arabic_hamzaunderalef', 'Arabic_hamzaonyeh', 'Arabic_alef', 'Arabic_beh', 'Arabic_tehmarbuta', 'Arabic_teh', 'Arabic_theh', 'Arabic_jeem', 'Arabic_hah', 'Arabic_khah', 'Arabic_dal', 'Arabic_thal', 'Arabic_ra', 'Arabic_zain', 'Arabic_seen', 'Arabic_sheen', 'Arabic_sad', 'Arabic_dad', 'Arabic_tah', 'Arabic_zah', 'Arabic_ain', 'Arabic_ghain', 'Arabic_tatweel', 'Arabic_feh', 'Arabic_qaf', 'Arabic_kaf', 'Arabic_lam', 'Arabic_meem', 'Arabic_noon', 'Arabic_heh', 'Arabic_waw', 'Arabic_alefmaksura', 'Arabic_yeh', 'Arabic_fathatan', 'Arabic_dammatan', 'Arabic_kasratan', 'Arabic_fatha', 'Arabic_damma', 'Arabic_kasra', 'Arabic_shadda', 'Arabic_sukun', 'Serbian_dje', 'Macedonia_gje', 'Cyrillic_io', 'Ukranian_je', 'Macedonia_dse', 'Ukranian_i', 'Ukranian_yi', 'Serbian_je', 'Serbian_lje', 'Serbian_nje', 'Serbian_tshe', 'Macedonia_kje', 'Byelorussian_shortu', 'Serbian_dze', 'numerosign', 'Serbian_DJE', 'Macedonia_GJE', 'Cyrillic_IO', 'Ukranian_JE', 'Macedonia_DSE', 'Ukranian_I', 'Ukranian_YI', 'Serbian_JE', 'Serbian_LJE', 'Serbian_NJE', 'Serbian_TSHE', 'Macedonia_KJE', 'Byelorussian_SHORTU', 'Serbian_DZE', 'Cyrillic_yu', 'Cyrillic_a', 'Cyrillic_be', 'Cyrillic_tse', 'Cyrillic_de', 'Cyrillic_ie', 'Cyrillic_ef', 'Cyrillic_ghe', 'Cyrillic_ha', 'Cyrillic_i', 'Cyrillic_shorti', 'Cyrillic_ka', 'Cyrillic_el', 'Cyrillic_em', 'Cyrillic_en', 'Cyrillic_o', 'Cyrillic_pe', 'Cyrillic_ya', 'Cyrillic_er', 'Cyrillic_es', 'Cyrillic_te', 'Cyrillic_u', 'Cyrillic_zhe', 'Cyrillic_ve', 'Cyrillic_softsign', 'Cyrillic_yeru', 'Cyrillic_ze', 'Cyrillic_sha', 'Cyrillic_e', 'Cyrillic_shcha', 'Cyrillic_che', 'Cyrillic_hardsign', 'Cyrillic_YU', 'Cyrillic_A', 'Cyrillic_BE', 'Cyrillic_TSE', 'Cyrillic_DE', 'Cyrillic_IE', 'Cyrillic_EF', 'Cyrillic_GHE', 'Cyrillic_HA', 'Cyrillic_I', 'Cyrillic_SHORTI', 'Cyrillic_KA', 'Cyrillic_EL', 'Cyrillic_EM', 'Cyrillic_EN', 'Cyrillic_O', 'Cyrillic_PE', 'Cyrillic_YA', 'Cyrillic_ER', 'Cyrillic_ES', 'Cyrillic_TE', 'Cyrillic_U', 'Cyrillic_ZHE', 'Cyrillic_VE', 'Cyrillic_SOFTSIGN', 'Cyrillic_YERU', 'Cyrillic_ZE', 'Cyrillic_SHA', 'Cyrillic_E', 'Cyrillic_SHCHA', 'Cyrillic_CHE', 'Cyrillic_HARDSIGN', 'Greek_ALPHAaccent', 'Greek_EPSILONaccent', 'Greek_ETAaccent', 'Greek_IOTAaccent', 'Greek_IOTAdiaeresis', 'Greek_IOTAaccentdiaeresis', 'Greek_OMICRONaccent', 'Greek_UPSILONaccent', 'Greek_UPSILONdieresis', 'Greek_UPSILONaccentdieresis', 'Greek_OMEGAaccent', 'Greek_alphaaccent', 'Greek_epsilonaccent', 'Greek_etaaccent', 'Greek_iotaaccent', 'Greek_iotadieresis', 'Greek_iotaaccentdieresis', 'Greek_omicronaccent', 'Greek_upsilonaccent', 'Greek_upsilondieresis', 'Greek_upsilonaccentdieresis', 'Greek_omegaaccent', 'Greek_ALPHA', 'Greek_BETA', 'Greek_GAMMA', 'Greek_DELTA', 'Greek_EPSILON', 'Greek_ZETA', 'Greek_ETA', 'Greek_THETA', 'Greek_IOTA', 'Greek_KAPPA', 'Greek_LAMBDA', 'Greek_MU', 'Greek_NU', 'Greek_XI', 'Greek_OMICRON', 'Greek_PI', 'Greek_RHO', 'Greek_SIGMA', 'Greek_TAU', 'Greek_UPSILON', 'Greek_PHI', 'Greek_CHI', 'Greek_PSI', 'Greek_OMEGA', 'Greek_alpha', 'Greek_beta', 'Greek_gamma', 'Greek_delta', 'Greek_epsilon', 'Greek_zeta', 'Greek_eta', 'Greek_theta', 'Greek_iota', 'Greek_kappa', 'Greek_lambda', 'Greek_mu', 'Greek_nu', 'Greek_xi', 'Greek_omicron', 'Greek_pi', 'Greek_rho', 'Greek_sigma', 'Greek_finalsmallsigma', 'Greek_tau', 'Greek_upsilon', 'Greek_phi', 'Greek_chi', 'Greek_psi', 'Greek_omega', 'leftradical', 'topleftradical', 'horizconnector', 'topintegral', 'botintegral', 'vertconnector', 'topleftsqbracket', 'botleftsqbracket', 'toprightsqbracket', 'botrightsqbracket', 'topleftparens', 'botleftparens', 'toprightparens', 'botrightparens', 'leftmiddlecurlybrace', 'rightmiddlecurlybrace', 'topleftsummation', 'botleftsummation', 'topvertsummationconnector', 'botvertsummationconnector', 'toprightsummation', 'botrightsummation', 'rightmiddlesummation', 'lessthanequal', 'notequal', 'greaterthanequal', 'integral', 'therefore', 'variation', 'infinity', 'nabla', 'approximate', 'similarequal', 'ifonlyif', 'implies', 'identical', 'radical', 'includedin', 'includes', 'intersection', 'union', 'logicaland', 'logicalor', 'partialderivative', 'function', 'leftarrow', 'uparrow', 'rightarrow', 'downarrow', 'blank', 'soliddiamond', 'checkerboard', 'ht', 'ff', 'cr', 'lf', 'nl', 'vt', 'lowrightcorner', 'uprightcorner', 'upleftcorner', 'lowleftcorner', 'crossinglines', 'horizlinescan1', 'horizlinescan3', 'horizlinescan5', 'horizlinescan7', 'horizlinescan9', 'leftt', 'rightt', 'bott', 'topt', 'vertbar', 'emspace', 'enspace', 'em3space', 'em4space', 'digitspace', 'punctspace', 'thinspace', 'hairspace', 'emdash', 'endash', 'signifblank', 'ellipsis', 'doubbaselinedot', 'onethird', 'twothirds', 'onefifth', 'twofifths', 'threefifths', 'fourfifths', 'onesixth', 'fivesixths', 'careof', 'figdash', 'leftanglebracket', 'decimalpoint', 'rightanglebracket', 'marker', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'trademark', 'signaturemark', 'trademarkincircle', 'leftopentriangle', 'rightopentriangle', 'emopencircle', 'emopenrectangle', 'leftsinglequotemark', 'rightsinglequotemark', 'leftdoublequotemark', 'rightdoublequotemark', 'prescription', 'minutes', 'seconds', 'latincross', 'hexagram', 'filledrectbullet', 'filledlefttribullet', 'filledrighttribullet', 'emfilledcircle', 'emfilledrect', 'enopencircbullet', 'enopensquarebullet', 'openrectbullet', 'opentribulletup', 'opentribulletdown', 'openstar', 'enfilledcircbullet', 'enfilledsqbullet', 'filledtribulletup', 'filledtribulletdown', 'leftpointer', 'rightpointer', 'club', 'diamond', 'heart', 'maltesecross', 'dagger', 'doubledagger', 'checkmark', 'ballotcross', 'musicalsharp', 'musicalflat', 'malesymbol', 'femalesymbol', 'telephone', 'telephonerecorder', 'phonographcopyright', 'caret', 'singlelowquotemark', 'doublelowquotemark', 'cursor', 'leftcaret', 'rightcaret', 'downcaret', 'upcaret', 'overbar', 'downtack', 'upshoe', 'downstile', 'underbar', 'jot', 'quad', 'uptack', 'circle', 'upstile', 'downshoe', 'rightshoe', 'leftshoe', 'lefttack', 'righttack', 'hebrew_aleph', 'hebrew_beth', 'hebrew_gimmel', 'hebrew_daleth', 'hebrew_he', 'hebrew_waw', 'hebrew_zayin', 'hebrew_het', 'hebrew_teth', 'hebrew_yod', 'hebrew_finalkaph', 'hebrew_kaph', 'hebrew_lamed', 'hebrew_finalmem', 'hebrew_mem', 'hebrew_finalnun', 'hebrew_nun', 'hebrew_samekh', 'hebrew_ayin', 'hebrew_finalpe', 'hebrew_pe', 'hebrew_finalzadi', 'hebrew_zadi', 'hebrew_kuf', 'hebrew_resh', 'hebrew_shin', 'hebrew_taf', 'BackSpace', 'Tab', 'Linefeed', 'Clear', 'Return', 'Pause', 'Scroll_Lock', 'Sys_Req', 'Escape', 'Multi_key', 'Kanji', 'Home', 'Left', 'Up', 'Right', 'Down', 'Prior', 'Next', 'End', 'Begin', 'Win_L', 'Win_R', 'App', 'Select', 'Print', 'Execute', 'Insert', 'Undo', 'Redo', 'Menu', 'Find', 'Cancel', 'Help', 'Break', 'Hebrew_switch', 'Num_Lock', 'KP_Space', 'KP_Tab', 'KP_Enter', 'KP_F1', 'KP_F2', 'KP_F3', 'KP_F4', 'KP_Multiply', 'KP_Add', 'KP_Separator', 'KP_Subtract', 'KP_Decimal', 'KP_Divide', 'KP_0', 'KP_1', 'KP_2', 'KP_3', 'KP_4', 'KP_5', 'KP_6', 'KP_7', 'KP_8', 'KP_9', 'KP_Equal', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7', 'L8', 'L9', 'L10', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'R10', 'R11', 'R12', 'F33', 'R14', 'R15', 'Shift_L', 'Shift_R', 'Control_L', 'Control_R', 'Caps_Lock', 'Shift_Lock', 'Meta_L', 'Meta_R', 'Alt_L', 'Alt_R', 'Super_L', 'Super_R', 'Hyper_L', 'Hyper_R', 'Delete',)
# fmt: on

# ------------------------------------------------------------------------- #
#                       ToolTip used by the Elements                        #
# ------------------------------------------------------------------------- #


class ToolTip:
    """
    Create a tooltip for a given widget
    (inspired by https://stackoverflow.com/a/36221216)
    This is an INTERNALLY USED only class.  Users should not refer to this class at all.
    """

    def __init__(self, widget, text, timeout=DEFAULT_TOOLTIP_TIME):
        """
        :param widget:  The tkinter widget
        :type widget:   widget type varies
        :param text:    text for the tooltip. It can inslude \n
        :type text:     (str)
        :param timeout: Time in milliseconds that mouse must remain still before tip is shown
        :type timeout:  (int)
        """
        self.widget = widget
        self.text = text
        self.timeout = timeout
        # self.wraplength = wraplength if wraplength else widget.winfo_screenwidth() // 2
        self.tipwindow = None
        self.id = None
        self.x = self.y = 0
        self.widget.bind('<Enter>', self.enter)
        self.widget.bind('<Leave>', self.leave)
        self.widget.bind('<ButtonPress>', self.leave)

    def enter(self, event=None):
        """
        Called by tkinter when mouse enters a widget
        :param event: from tkinter.  Has x,y coordinates of mouse
        :type event:

        """
        self.x = event.x
        self.y = event.y
        self.schedule()

    def leave(self, event=None):
        """
        Called by tktiner when mouse exits a widget
        :param event: from tkinter.  Event info that's not used by function.
        :type event:

        """
        self.unschedule()
        self.hidetip()

    def schedule(self):
        """
        Schedule a timer to time how long mouse is hovering
        """
        self.unschedule()
        self.id = self.widget.after(self.timeout, self.showtip)

    def unschedule(self):
        """
        Cancel timer used to time mouse hover
        """
        if self.id:
            self.widget.after_cancel(self.id)
        self.id = None

    def showtip(self):
        """
        Creates a topoltip window with the tooltip text inside of it
        """
        if self.tipwindow:
            return
        x = self.widget.winfo_rootx() + self.x + DEFAULT_TOOLTIP_OFFSET[0]
        y = self.widget.winfo_rooty() + self.y + DEFAULT_TOOLTIP_OFFSET[1]
        self.tipwindow = tk.Toplevel(self.widget)
        # if not sys.platform.startswith('darwin'):
        try:
            self.tipwindow.wm_overrideredirect(True)
            # if running_mac() and ENABLE_MAC_NOTITLEBAR_PATCH:
            if _mac_should_apply_notitlebar_patch():
                self.tipwindow.wm_overrideredirect(False)
        except Exception as e:
            print('* Error performing wm_overrideredirect in showtip *', e)
        self.tipwindow.wm_geometry('+%d+%d' % (x, y))
        self.tipwindow.wm_attributes('-topmost', 1)

        label = ttk.Label(
            self.tipwindow,
            text=self.text,
            justify=tk.LEFT,
            background=TOOLTIP_BACKGROUND_COLOR,
            relief=tk.SOLID,
            borderwidth=1,
        )
        if TOOLTIP_FONT is not None:
            label.config(font=TOOLTIP_FONT)
        label.pack()

    def hidetip(self):
        """
        Destroy the tooltip window
        """
        if self.tipwindow:
            self.tipwindow.destroy()
        self.tipwindow = None


class _TimerPeriodic:
    id_counter = 1
    # Dictionary containing the active timers.  Format is {id : _TimerPeriodic object}
    active_timers = {}  # type: dict[int:_TimerPeriodic]

    def __init__(self, window, frequency_ms, key=EVENT_TIMER, repeating=True):
        """
        :param window:          The window to send events to
        :type window:           FreeSimpleGUI.window.Window
        :param frequency_ms:    How often to send events in milliseconds
        :type frequency_ms:     int
        :param repeating:       If True then the timer will run, repeatedly sending events, until stopped
        :type repeating:        bool
        """
        self.window = window
        self.frequency_ms = frequency_ms
        self.repeating = repeating
        self.key = key
        self.id = _TimerPeriodic.id_counter
        _TimerPeriodic.id_counter += 1
        self.start()

    @classmethod
    def stop_timer_with_id(cls, timer_id):
        """
        Not user callable!
        :return: A simple counter that makes each container element unique
        :rtype:
        """
        timer = cls.active_timers.get(timer_id, None)
        if timer is not None:
            timer.stop()

    @classmethod
    def stop_all_timers_for_window(cls, window):
        """
        Stops all timers for a given window
        :param window:      The window to stop timers for
        :type window:       FreeSimpleGUI.window.Window
        """
        for timer in _TimerPeriodic.active_timers.values():
            if timer.window == window:
                timer.running = False

    @classmethod
    def get_all_timers_for_window(cls, window):
        """
        Returns a list of timer IDs for a given window
        :param window:      The window to find timers for
        :type window:       FreeSimpleGUI.window.Window
        :return:            List of timer IDs for the window
        :rtype:             List[int]
        """
        timers = []
        for timer in _TimerPeriodic.active_timers.values():
            if timer.window == window:
                timers.append(timer.id)

        return timers

    def timer_thread(self):
        """
        The thread that sends events to the window.  Runs either once or in a loop until timer is stopped
        """

        if not self.running:  # if timer has been cancelled, abort
            del _TimerPeriodic.active_timers[self.id]
            return
        while True:
            time.sleep(self.frequency_ms / 1000)
            if not self.running:  # if timer has been cancelled, abort
                del _TimerPeriodic.active_timers[self.id]
                return
            self.window.write_event_value(self.key, self.id)

            if not self.repeating:  # if timer does not repeat, then exit thread
                del _TimerPeriodic.active_timers[self.id]
                return

    def start(self):
        """
        Starts a timer by starting a timer thread
        Adds timer to the list of active timers
        """
        self.running = True
        self.thread = threading.Thread(target=self.timer_thread, daemon=True)
        self.thread.start()
        _TimerPeriodic.active_timers[self.id] = self

    def stop(self):
        """
        Stops a timer
        """
        self.running = False


def _long_func_thread(window, end_key, original_func):
    """
    Used to run long operations on the user's behalf. Called by the window object

    :param window:        The window that will get the event
    :type window:         (Window)
    :param end_key:       The event that will be sent when function returns. If None then no event will be sent when exiting thread
    :type end_key:        (Any|None)
    :param original_func: The user's function that is called. Can be a function with no arguments or a lambda experession
    :type original_func:  (Any)
    """

    return_value = original_func()
    if end_key is not None:
        window.write_event_value(end_key, return_value)


def _timeout_alarm_callback_hidden():
    """
    Read Timeout Alarm callback. Will kick a mainloop call out of the tkinter event loop and cause it to return
    """

    del Window._TKAfterID

    # first, get the results table built
    # modify the Results table in the parent FlexForm object
    # print('TIMEOUT CALLBACK')
    Window._root_running_mainloop.quit()  # kick the users out of the mainloop

    # Get window that caused return
    Window._window_that_exited = None


def read_all_windows(timeout=None, timeout_key=TIMEOUT_KEY):
    """
    Reads all windows that are "active" when the call is made. "Active" means that it's been finalized or read.
    If a window has not been finalized then it will not be considered an "active window"

    If any of the active windows returns a value then the window and its event and values
    are returned.

    If no windows are open, then the value (None, WIN_CLOSED, None) will be returned
        Since WIN_CLOSED is None, it means (None, None, None) is what's returned when no windows remain opened

    :param timeout:     Time in milliseconds to delay before a returning a timeout event
    :type timeout:      (int)
    :param timeout_key: Key to return when a timeout happens. Defaults to the standard TIMEOUT_KEY
    :type timeout_key:  (Any)
    :return:            A tuple with the  (Window, event, values dictionary/list)
    :rtype:             (Window, Any, Dict | List)
    """

    if len(Window._active_windows) == 0:
        return None, WIN_CLOSED, None

    # first see if any queued events are waiting for any of the windows
    for window in Window._active_windows.keys():
        if window._queued_thread_event_available():
            _BuildResults(window, False, window)
            event, values = window.ReturnValues
            return window, event, values

    Window._root_running_mainloop = Window.hidden_master_root
    Window._timeout_key = timeout_key

    if timeout == 0:
        window = list(Window._active_windows.keys())[Window._timeout_0_counter]
        event, values = window._ReadNonBlocking()
        if event is None:
            event = timeout_key
        if values is None:
            event = None
        Window._timeout_0_counter = (Window._timeout_0_counter + 1) % len(Window._active_windows)
        return window, event, values

    Window._timeout_0_counter = 0  # reset value if not reading with timeout 0 so ready next time needed

    # setup timeout timer
    if timeout is not None:
        try:
            Window.hidden_master_root.after_cancel(Window._TKAfterID)
            del Window._TKAfterID
        except:
            pass

        Window._TKAfterID = Window.hidden_master_root.after(timeout, _timeout_alarm_callback_hidden)

    # ------------ Call Mainloop ------------
    Window._root_running_mainloop.mainloop()

    try:
        Window.hidden_master_root.after_cancel(Window._TKAfterID)
        del Window._TKAfterID
    except:
        pass
        # print('** tkafter cancel failed **')

    # Get window that caused return

    window = Window._window_that_exited

    if window is None:
        return None, timeout_key, None

    if window.XFound:
        event, values = None, None
        window.close()
        try:
            del Window._active_windows[window]
        except:
            pass
            # print('Error deleting window, but OK')
    else:
        _BuildResults(window, False, window)
        event, values = window.ReturnValues

    return window, event, values


# =========================================================================== #
# Button Lazy Functions so the caller doesn't have to define a bunch of stuff #
# =========================================================================== #


# ------------------------- A fake Element... the Pad Element ------------------------- #
def Sizer(h_pixels=0, v_pixels=0):
    """
    "Pushes" out the size of whatever it is placed inside of.  This includes Columns, Frames, Tabs and Windows

    :param h_pixels: number of horizontal pixels
    :type h_pixels:  (int)
    :param v_pixels: number of vertical pixels
    :type v_pixels:  (int)
    :return:         (Canvas) A canvas element that has a pad setting set according to parameters
    :rtype:          (FreeSimpleGUI.elements.canvas.Canvas)
    """

    return Canvas(size=(0, 0), pad=((h_pixels, 0), (v_pixels, 0)))


def pin(elem, vertical_alignment=None, shrink=True, expand_x=None, expand_y=None):
    """
    Pin's an element provided into a layout so that when it's made invisible and visible again, it will
     be in the correct place.  Otherwise it will be placed at the end of its containing window/column.

     The element you want to pin is the element that you'll be making visibile/invisible.

    The pin helper function also causes containers to shrink to fit the contents correct after something inside
     has changed visiblity.  Note that setting a hardcoded size on your window can impact this ability to shrink.

    :param elem:               the element to put into the layout
    :type elem:                Element
    :param vertical_alignment: Aligns elements vertically. 'top', 'center', 'bottom'. Can be shortened to 't', 'c', 'b'
    :type vertical_alignment:  str | None
    :param shrink:             If True, then the space will shrink down to a single pixel when hidden. False leaves the area large and blank
    :type shrink:              bool
    :param expand_x:           If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_x:            (bool)
    :param expand_y:           If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_y:            (bool)
    :return:                   A column element containing the provided element
    :rtype:                    Column
    """
    if shrink:
        # return Column([[elem, Canvas(size=(0, 0),background_color=elem.BackgroundColor, pad=(0, 0))]], pad=(0, 0), vertical_alignment=vertical_alignment, expand_x=expand_x, expand_y=expand_y)
        return Column(
            [[elem, Column([[]], pad=(0, 0))]],
            pad=(0, 0),
            vertical_alignment=vertical_alignment,
            expand_x=expand_x,
            expand_y=expand_y,
        )
    else:
        return Column([[elem]], pad=(0, 0), vertical_alignment=vertical_alignment, expand_x=expand_x, expand_y=expand_y)


def vtop(elem_or_row, expand_x=None, expand_y=None, background_color=None):
    """
    Align an element or a row of elements to the top of the row that contains it

    :param elem_or_row:      the element or row of elements
    :type elem_or_row:       Element | List[Element] | Tuple[Element]
    :param expand_x:         If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_x:          (bool)
    :param expand_y:         If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_y:          (bool)
    :param background_color: Background color for container that is used by vtop to do the alignment
    :type background_color:  str | None
    :return:                 A column element containing the provided element aligned to the top or list of elements (a row)
    :rtype:                  Column | List[Column]
    """

    if isinstance(elem_or_row, list) or isinstance(elem_or_row, tuple):
        return [
            Column(
                [[e]],
                pad=(0, 0),
                vertical_alignment='top',
                expand_x=expand_x,
                expand_y=expand_y,
                background_color=background_color,
            )
            for e in elem_or_row
        ]

    return Column(
        [[elem_or_row]],
        pad=(0, 0),
        vertical_alignment='top',
        expand_x=expand_x,
        expand_y=expand_y,
        background_color=background_color,
    )


def vcenter(elem_or_row, expand_x=None, expand_y=None, background_color=None):
    """
    Align an element or a row of elements to the center of the row that contains it

    :param elem_or_row:      the element or row of elements
    :type elem_or_row:       Element | List[Element] | Tuple[Element]
    :param expand_x:         If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_x:          (bool)
    :param expand_y:         If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_y:          (bool)
    :param background_color: Background color for container that is used by vcenter to do the alignment
    :type background_color:  str | None
    :return:                 A column element containing the provided element aligned to the center or list of elements (a row)
    :rtype:                  Column | List[Column]
    """

    if isinstance(elem_or_row, list) or isinstance(elem_or_row, tuple):
        return [
            Column(
                [[e]],
                pad=(0, 0),
                vertical_alignment='center',
                expand_x=expand_x,
                expand_y=expand_y,
                background_color=background_color,
            )
            for e in elem_or_row
        ]

    return Column(
        [[elem_or_row]],
        pad=(0, 0),
        vertical_alignment='center',
        expand_x=expand_x,
        expand_y=expand_y,
        background_color=background_color,
    )


def vbottom(elem_or_row, expand_x=None, expand_y=None, background_color=None):
    """
    Align an element or a row of elements to the bottom of the row that contains it

    :param elem_or_row:      the element or row of elements
    :type elem_or_row:       Element | List[Element] | Tuple[Element]
    :param expand_x:         If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_x:          (bool)
    :param expand_y:         If True/False the value will be passed to the Column Elements used to make this feature
    :type expand_y:          (bool)
    :param background_color: Background color for container that is used by vcenter to do the alignment
    :type background_color:  str | None
    :return:                 A column element containing the provided element aligned to the bottom or list of elements (a row)
    :rtype:                  Column | List[Column]
    """

    if isinstance(elem_or_row, list) or isinstance(elem_or_row, tuple):
        return [
            Column(
                [[e]],
                pad=(0, 0),
                vertical_alignment='bottom',
                expand_x=expand_x,
                expand_y=expand_y,
                background_color=background_color,
            )
            for e in elem_or_row
        ]

    return Column(
        [[elem_or_row]],
        pad=(0, 0),
        vertical_alignment='bottom',
        expand_x=expand_x,
        expand_y=expand_y,
        background_color=background_color,
    )


def Titlebar(title='', icon=None, text_color=None, background_color=None, font=None, key=None, k=None):
    """
    A custom titlebar that replaces the OS provided titlebar, thus giving you control
    the is not possible using the OS provided titlebar such as the color.

    NOTE LINUX USERS - at the moment the minimize function is not yet working.  Windows users
    should have no problem and it should function as a normal window would.

    This titlebar is created from a row of elements that is then encapsulated into a
    one Column element which is what this Titlebar function returns to you.

    A custom titlebar removes the margins from your window.  If you want the  remainder
    of your Window to have margins, place the layout after the Titlebar into a Column and
    set the pad of that Column to the dimensions you would like your margins to have.

    The Titlebar is a COLUMN element.  You can thus call the update method for the column and
    perform operations such as making the column visible/invisible

    :param icon:             Can be either a filename or Base64 byte string of a PNG or GIF. This is used in an Image element to create the titlebar
    :type icon:              str or bytes or None
    :param title:            The "title" to show in the titlebar
    :type title:             str
    :param text_color:       Text color for titlebar
    :type text_color:        str | None
    :param background_color: Background color for titlebar
    :type background_color:  str | None
    :param font:             Font to be used for the text and the symbols
    :type font:              (str or (str, int[, str]) or None)
    :param key:              Identifies an Element. Should be UNIQUE to this window.
    :type key:               str | int | tuple | object | None
    :param k:                Exactly the same as key.  Choose one of them to use
    :type k:                 str | int | tuple | object | None
    :return:                 A single Column element that has eveything in 1 element
    :rtype:                  Column
    """
    bc = background_color or CUSTOM_TITLEBAR_BACKGROUND_COLOR or theme_button_color()[1]
    tc = text_color or CUSTOM_TITLEBAR_TEXT_COLOR or theme_button_color()[0]
    font = font or CUSTOM_TITLEBAR_FONT or ('Helvetica', 12)
    key = k or key

    if isinstance(icon, bytes):
        icon_and_text_portion = [Image(data=icon, background_color=bc, key=TITLEBAR_IMAGE_KEY)]
    elif icon == TITLEBAR_DO_NOT_USE_AN_ICON:
        icon_and_text_portion = []
    elif icon is not None:
        icon_and_text_portion = [Image(filename=icon, background_color=bc, key=TITLEBAR_IMAGE_KEY)]
    elif CUSTOM_TITLEBAR_ICON is not None:
        if isinstance(CUSTOM_TITLEBAR_ICON, bytes):
            icon_and_text_portion = [Image(data=CUSTOM_TITLEBAR_ICON, background_color=bc, key=TITLEBAR_IMAGE_KEY)]
        else:
            icon_and_text_portion = [Image(filename=CUSTOM_TITLEBAR_ICON, background_color=bc, key=TITLEBAR_IMAGE_KEY)]
    else:
        icon_and_text_portion = [Image(data=DEFAULT_BASE64_ICON_16_BY_16, background_color=bc, key=TITLEBAR_IMAGE_KEY)]

    icon_and_text_portion += [T(title, text_color=tc, background_color=bc, font=font, grab=True, key=TITLEBAR_TEXT_KEY)]

    return Column(
        [
            [
                Column([icon_and_text_portion], pad=(0, 0), background_color=bc),
                Column(
                    [
                        [
                            T(
                                SYMBOL_TITLEBAR_MINIMIZE,
                                text_color=tc,
                                background_color=bc,
                                enable_events=True,
                                font=font,
                                key=TITLEBAR_MINIMIZE_KEY,
                            ),
                            Text(
                                SYMBOL_TITLEBAR_MAXIMIZE,
                                text_color=tc,
                                background_color=bc,
                                enable_events=True,
                                font=font,
                                key=TITLEBAR_MAXIMIZE_KEY,
                            ),
                            Text(
                                SYMBOL_TITLEBAR_CLOSE,
                                text_color=tc,
                                background_color=bc,
                                font=font,
                                enable_events=True,
                                key=TITLEBAR_CLOSE_KEY,
                            ),
                        ]
                    ],
                    element_justification='r',
                    expand_x=True,
                    grab=True,
                    pad=(0, 0),
                    background_color=bc,
                ),
            ]
        ],
        expand_x=True,
        grab=True,
        background_color=bc,
        pad=(0, 0),
        metadata=TITLEBAR_METADATA_MARKER,
        key=key,
    )


def MenubarCustom(
    menu_definition,
    disabled_text_color=None,
    bar_font=None,
    font=None,
    tearoff=False,
    pad=0,
    p=None,
    background_color=None,
    text_color=None,
    bar_background_color=None,
    bar_text_color=None,
    key=None,
    k=None,
):
    """
    A custom Menubar that replaces the OS provided Menubar

    Why?
    Two reasons - 1. they look great (see custom titlebar) 2. if you have a custom titlebar, then you have to use a custom menubar if you want a menubar

    :param menu_definition:      The Menu definition specified using lists (docs explain the format)
    :type menu_definition:       List[List[Tuple[str, List[str]]]
    :param disabled_text_color:  color to use for text when item is disabled. Can be in #RRGGBB format or a color name "black"
    :type disabled_text_color:   (str)
    :param bar_font:             specifies the font family, size to be used for the chars in the bar itself
    :type bar_font:              (str or (str, int[, str]) or None)
    :param font:                 specifies the font family, size to be used for the menu items
    :type font:                  (str or (str, int[, str]) or None)
    :param tearoff:              if True, then can tear the menu off from the window ans use as a floating window. Very cool effect
    :type tearoff:               (bool)
    :param pad:                  Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int).  TIP - 0 will make flush with titlebar
    :type pad:                   (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                    Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                     (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param background_color:     color to use for background of the menus that are displayed after making a section. Can be in #RRGGBB format or a color name "black". Defaults to the color of the bar text
    :type background_color:      (str)
    :param text_color:           color to use for the text of the many items in the displayed menus. Can be in #RRGGBB format or a color name "black". Defaults to the bar background
    :type text_color:            (str)
    :param bar_background_color: color to use for the menubar. Can be in #RRGGBB format or a color name "black". Defaults to theme's button text color
    :type bar_background_color:  (str)
    :param bar_text_color:       color to use for the menu items text when item is disabled. Can be in #RRGGBB format or a color name "black". Defaults to theme's button background color
    :type bar_text_color:        (str)
    :param key:                  Value that uniquely identifies this element from all other elements. Used when Finding an element or in return values. Must be unique to the window
    :type key:                   str | int | tuple | object
    :param k:                    Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                     str | int | tuple | object
    :returns:                    A Column element that has a series of ButtonMenu elements
    :rtype:                      Column
    """

    bar_bg = bar_background_color if bar_background_color is not None else theme_button_color()[0]
    bar_text = bar_text_color if bar_text_color is not None else theme_button_color()[1]
    menu_bg = background_color if background_color is not None else bar_text
    menu_text = text_color if text_color is not None else bar_bg
    pad = pad if pad is not None else p

    row = []
    for menu in menu_definition:
        text = menu[0]
        if MENU_SHORTCUT_CHARACTER in text:
            text = text.replace(MENU_SHORTCUT_CHARACTER, '')
        if text.startswith(MENU_DISABLED_CHARACTER):
            disabled = True
            text = text[len(MENU_DISABLED_CHARACTER) :]
        else:
            disabled = False

        button_menu = ButtonMenu(
            text,
            menu,
            border_width=0,
            button_color=(bar_text, bar_bg),
            key=text,
            pad=(0, 0),
            disabled=disabled,
            font=bar_font,
            item_font=font,
            disabled_text_color=disabled_text_color,
            text_color=menu_text,
            background_color=menu_bg,
            tearoff=tearoff,
        )
        button_menu.part_of_custom_menubar = True
        button_menu.custom_menubar_key = key if key is not None else k
        row += [button_menu]
    return Column([row], pad=pad, background_color=bar_bg, expand_x=True, key=key if key is not None else k)


# -------------------------  FOLDER BROWSE Element lazy function  ------------------------- #
def FolderBrowse(
    button_text='Browse',
    target=(ThisRow, -1),
    initial_folder=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    change_submits=False,
    enable_events=False,
    font=None,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    :param button_text:      text in the button (Default value = 'Browse')
    :type button_text:       (str)
    :param target:           target for the button (Default value = (ThisRow, -1))
    :type target:            str | (int, int)
    :param initial_folder:   starting path for folders and files
    :type initial_folder:    (str)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param change_submits:   If True, pressing Enter key submits window (Default = False)
    :type enable_events:     (bool)
    :param enable_events:    Turns on the element specific events.(Default = False)
    :type enable_events:     (bool)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              Used with window.find_element and with return values to uniquely identify this element
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 The Button created
    :rtype:                  (Button)
    """

    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_BROWSE_FOLDER,
        target=target,
        initial_folder=initial_folder,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        disabled=disabled,
        button_color=button_color,
        change_submits=change_submits,
        enable_events=enable_events,
        font=font,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  FILE BROWSE Element lazy function  ------------------------- #
def FileBrowse(
    button_text='Browse',
    target=(ThisRow, -1),
    file_types=FILE_TYPES_ALL_FILES,
    initial_folder=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    change_submits=False,
    enable_events=False,
    font=None,
    disabled=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Browse')
    :type button_text:       (str)
    :param target:           key or (row,col) target for the button (Default value = (ThisRow, -1))
    :type target:            str | (int, int)
    :param file_types:       filter file types Default value = (("ALL Files", "*.* *"),).
    :type file_types:        Tuple[(str, str), ...]
    :param initial_folder:   starting path for folders and files
    :type initial_folder:
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param change_submits:   If True, pressing Enter key submits window (Default = False)
    :type change_submits:    (bool)
    :param enable_events:    Turns on the element specific events.(Default = False)
    :type enable_events:     (bool)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_BROWSE_FILE,
        target=target,
        file_types=file_types,
        initial_folder=initial_folder,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        change_submits=change_submits,
        enable_events=enable_events,
        disabled=disabled,
        button_color=button_color,
        font=font,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  FILES BROWSE Element (Multiple file selection) lazy function  ------------------------- #
def FilesBrowse(
    button_text='Browse',
    target=(ThisRow, -1),
    file_types=FILE_TYPES_ALL_FILES,
    disabled=False,
    initial_folder=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    change_submits=False,
    enable_events=False,
    font=None,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    files_delimiter=BROWSE_FILES_DELIMITER,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    Allows browsing of multiple files. File list is returned as a single list with the delimiter defined using the files_delimiter parameter.

    :param button_text:      text in the button (Default value = 'Browse')
    :type button_text:       (str)
    :param target:           key or (row,col) target for the button (Default value = (ThisRow, -1))
    :type target:            str | (int, int)
    :param file_types:       Default value = (("ALL Files", "*.* *"),).
    :type file_types:        Tuple[(str, str), ...]
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param initial_folder:   starting path for folders and files
    :type initial_folder:    (str)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param change_submits:   If True, pressing Enter key submits window (Default = False)
    :type change_submits:    (bool)
    :param enable_events:    Turns on the element specific events.(Default = False)
    :type enable_events:     (bool)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param files_delimiter:  String to place between files when multiple files are selected. Normally a ;
    :type files_delimiter:   str
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    button = Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_BROWSE_FILES,
        target=target,
        file_types=file_types,
        initial_folder=initial_folder,
        change_submits=change_submits,
        enable_events=enable_events,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        disabled=disabled,
        button_color=button_color,
        font=font,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )
    button._files_delimiter = files_delimiter
    return button


# -------------------------  FILE BROWSE Element lazy function  ------------------------- #
def FileSaveAs(
    button_text='Save As...',
    target=(ThisRow, -1),
    file_types=FILE_TYPES_ALL_FILES,
    initial_folder=None,
    default_extension='',
    disabled=False,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    change_submits=False,
    enable_events=False,
    font=None,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:       text in the button (Default value = 'Save As...')
    :type button_text:        (str)
    :param target:            key or (row,col) target for the button (Default value = (ThisRow, -1))
    :type target:             str | (int, int)
    :param file_types:        Default value = (("ALL Files", "*.* *"),).
    :type file_types:         Tuple[(str, str), ...]
    :param default_extension: If no extension entered by user, add this to filename (only used in saveas dialogs)
    :type default_extension:  (str)
    :param initial_folder:    starting path for folders and files
    :type initial_folder:     (str)
    :param disabled:          set disable state for element (Default = False)
    :type disabled:           (bool)
    :param tooltip:           text, that will appear when mouse hovers over the element
    :type tooltip:            (str)
    :param size:              (w,h) w=characters-wide, h=rows-high
    :type size:               (int, int)
    :param s:                 Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                  (int, int)  | (None, None) | int
    :param auto_size_button:  True if button size is determined by button text
    :type auto_size_button:   (bool)
    :param button_color:      button color (foreground, background)
    :type button_color:       (str, str) | str
    :param change_submits:    If True, pressing Enter key submits window (Default = False)
    :type change_submits:     (bool)
    :param enable_events:     Turns on the element specific events.(Default = False)
    :type enable_events:      (bool)
    :param font:              specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:               (str or (str, int[, str]) or None)
    :param pad:               Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:                (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                 Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                  (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:               key for uniquely identify this element (for window.find_element)
    :type key:                str | int | tuple | object
    :param k:                 Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                  str | int | tuple | object
    :param visible:           set initial visibility state of the Button
    :type visible:            (bool)
    :param metadata:          Anything you want to store along with this button
    :type metadata:           (Any)
    :param expand_x:          If True Element will expand in the Horizontal directions
    :type expand_x:           (bool)
    :param expand_y:          If True Element will expand in the Vertical directions
    :type expand_y:           (bool)        :return:                  returns a button
    :rtype:                   (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_SAVEAS_FILE,
        target=target,
        file_types=file_types,
        initial_folder=initial_folder,
        default_extension=default_extension,
        tooltip=tooltip,
        size=size,
        s=s,
        disabled=disabled,
        auto_size_button=auto_size_button,
        button_color=button_color,
        change_submits=change_submits,
        enable_events=enable_events,
        font=font,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  SAVE AS Element lazy function  ------------------------- #
def SaveAs(
    button_text='Save As...',
    target=(ThisRow, -1),
    file_types=FILE_TYPES_ALL_FILES,
    initial_folder=None,
    default_extension='',
    disabled=False,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    change_submits=False,
    enable_events=False,
    font=None,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:       text in the button (Default value = 'Save As...')
    :type button_text:        (str)
    :param target:            key or (row,col) target for the button (Default value = (ThisRow, -1))
    :type target:             str | (int, int)
    :param file_types:        Default value = (("ALL Files", "*.* *"),).
    :type file_types:         Tuple[(str, str), ...]
    :param default_extension: If no extension entered by user, add this to filename (only used in saveas dialogs)
    :type default_extension:  (str)
    :param initial_folder:    starting path for folders and files
    :type initial_folder:     (str)
    :param disabled:          set disable state for element (Default = False)
    :type disabled:           (bool)
    :param tooltip:           text, that will appear when mouse hovers over the element
    :type tooltip:            (str)
    :param size:              (w,h) w=characters-wide, h=rows-high
    :type size:               (int, int)
    :param s:                 Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                  (int, int)  | (None, None) | int
    :param auto_size_button:  True if button size is determined by button text
    :type auto_size_button:   (bool)
    :param button_color:      button color (foreground, background)
    :type button_color:       (str, str) or str
    :param change_submits:    If True, pressing Enter key submits window (Default = False)
    :type change_submits:     (bool)
    :param enable_events:     Turns on the element specific events.(Default = False)
    :type enable_events:      (bool)
    :param font:              specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:               (str or (str, int[, str]) or None)
    :param pad:               Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:                (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                 Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                  (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int    :param key:               key for uniquely identify this element (for window.find_element)
    :type key:                str | int | tuple | object
    :param k:                 Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                  str | int | tuple | object
    :param visible:           set initial visibility state of the Button
    :type visible:            (bool)
    :param metadata:          Anything you want to store along with this button
    :type metadata:           (Any)
    :param expand_x:          If True Element will expand in the Horizontal directions
    :type expand_x:           (bool)
    :param expand_y:          If True Element will expand in the Vertical directions
    :type expand_y:           (bool)
    :return:                  returns a button
    :rtype:                   (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_SAVEAS_FILE,
        target=target,
        file_types=file_types,
        initial_folder=initial_folder,
        default_extension=default_extension,
        tooltip=tooltip,
        size=size,
        s=s,
        disabled=disabled,
        auto_size_button=auto_size_button,
        button_color=button_color,
        change_submits=change_submits,
        enable_events=enable_events,
        font=font,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  SAVE BUTTON Element lazy function  ------------------------- #
def Save(
    button_text='Save',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    bind_return_key=True,
    disabled=False,
    tooltip=None,
    font=None,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Save')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param bind_return_key:  (Default = True) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  SUBMIT BUTTON Element lazy function  ------------------------- #
def Submit(
    button_text='Submit',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    bind_return_key=True,
    tooltip=None,
    font=None,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Submit')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param bind_return_key:  (Default = True) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  OPEN BUTTON Element lazy function  ------------------------- #
# -------------------------  OPEN BUTTON Element lazy function  ------------------------- #
def Open(
    button_text='Open',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    bind_return_key=True,
    tooltip=None,
    font=None,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Open')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param bind_return_key:  (Default = True) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  OK BUTTON Element lazy function  ------------------------- #
def OK(
    button_text='OK',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    bind_return_key=True,
    tooltip=None,
    font=None,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'OK')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param bind_return_key:  (Default = True) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  YES BUTTON Element lazy function  ------------------------- #
def Ok(
    button_text='Ok',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    bind_return_key=True,
    tooltip=None,
    font=None,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Ok')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param bind_return_key:  (Default = True) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  CANCEL BUTTON Element lazy function  ------------------------- #
def Cancel(
    button_text='Cancel',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    tooltip=None,
    font=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Cancel')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  QUIT BUTTON Element lazy function  ------------------------- #
def Quit(
    button_text='Quit',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    tooltip=None,
    font=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Quit')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:             (bool)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  Exit BUTTON Element lazy function  ------------------------- #
def Exit(
    button_text='Exit',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    tooltip=None,
    font=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Exit')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  YES BUTTON Element lazy function  ------------------------- #
def Yes(
    button_text='Yes',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    tooltip=None,
    font=None,
    bind_return_key=True,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Yes')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = True) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  NO BUTTON Element lazy function  ------------------------- #
def No(
    button_text='No',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    tooltip=None,
    font=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'No')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, then the return key will cause a the Listbox to generate an event
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  NO BUTTON Element lazy function  ------------------------- #
def Help(
    button_text='Help',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    font=None,
    tooltip=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button (Default value = 'Help')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  NO BUTTON Element lazy function  ------------------------- #
def Debug(
    button_text='',
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    font=None,
    tooltip=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    This Button has been changed in how it works!!
    Your button has been replaced with a normal button that has the PySimpleGUI Debugger buggon logo on it.
    In your event loop, you will need to check for the event of this button and then call:
            show_debugger_popout_window()
    :param button_text:      text in the button (Default value = '')
    :type button_text:       (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """

    user_key = key if key is not None else k if k is not None else button_text

    return Button(
        button_text='',
        button_type=BUTTON_TYPE_READ_FORM,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=theme_button_color(),
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=user_key,
        k=k,
        visible=visible,
        image_data=PSG_DEBUGGER_LOGO,
        image_subsample=2,
        border_width=0,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  GENERIC BUTTON Element lazy function  ------------------------- #
def SimpleButton(
    button_text,
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    border_width=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    font=None,
    bind_return_key=False,
    disabled=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    DEPIRCATED

    This Button should not be used.

    :param button_text:      text in the button
    :type button_text:       (str)
    :param image_filename:   image filename if there is a button image
    :type image_filename:    image filename if there is a button image
    :param image_data:       in-RAM image to be displayed on button
    :type image_data:        in-RAM image to be displayed on button
    :param image_size:       image size (O.K.)
    :type image_size:        (Default = (None))
    :param image_subsample:  amount to reduce the size of the image
    :type image_subsample:   amount to reduce the size of the image
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_CLOSES_WIN,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        disabled=disabled,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  CLOSE BUTTON Element lazy function  ------------------------- #
def CloseButton(
    button_text,
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    border_width=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    font=None,
    bind_return_key=False,
    disabled=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    DEPRICATED

    This button should not be used.  Instead explicitly close your windows by calling window.close() or by using
    the close parameter in window.read

    :param button_text:      text in the button
    :type button_text:       (str)
    :param image_filename:   image filename if there is a button image
    :type image_filename:    image filename if there is a button image
    :param image_data:       in-RAM image to be displayed on button
    :type image_data:        in-RAM image to be displayed on button
    :param image_size:       image size (O.K.)
    :type image_size:        (Default = (None))
    :param image_subsample:  amount to reduce the size of the image
    :type image_subsample:   amount to reduce the size of the image
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_CLOSES_WIN,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        disabled=disabled,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


CButton = CloseButton


# -------------------------  GENERIC BUTTON Element lazy function  ------------------------- #
def ReadButton(
    button_text,
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    border_width=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    font=None,
    bind_return_key=False,
    disabled=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    :param button_text:      text in the button
    :type button_text:       (str)
    :param image_filename:   image filename if there is a button image
    :type image_filename:    image filename if there is a button image
    :param image_data:       in-RAM image to be displayed on button
    :type image_data:        in-RAM image to be displayed on button
    :param image_size:       image size (O.K.)
    :type image_size:        (Default = (None))
    :param image_subsample:  amount to reduce the size of the image
    :type image_subsample:   amount to reduce the size of the image
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param focus:            if focus should be set to this
    :type focus:             idk_yetReally
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param border_width:     width of border around element
    :type border_width:      (int)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 Button created
    :rtype:                  (Button)
    """

    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_READ_FORM,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        size=size,
        s=s,
        disabled=disabled,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


ReadFormButton = ReadButton
RButton = ReadFormButton


# -------------------------  Realtime BUTTON Element lazy function  ------------------------- #
def RealtimeButton(
    button_text,
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    border_width=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    font=None,
    disabled=False,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button
    :type button_text:       (str)
    :param image_filename:   image filename if there is a button image
    :type image_filename:    image filename if there is a button image
    :param image_data:       in-RAM image to be displayed on button
    :type image_data:        in-RAM image to be displayed on button
    :param image_size:       image size (O.K.)
    :type image_size:        (Default = (None))
    :param image_subsample:  amount to reduce the size of the image
    :type image_subsample:   amount to reduce the size of the image
    :param border_width:     width of border around element
    :type border_width:      (int)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:             (bool)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 Button created
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_REALTIME,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        disabled=disabled,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  Dummy BUTTON Element lazy function  ------------------------- #
def DummyButton(
    button_text,
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    border_width=None,
    tooltip=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    font=None,
    disabled=False,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    This is a special type of Button.

    It will close the window but NOT send an event that the window has been closed.

    It's used in conjunction with non-blocking windows to silently close them.  They are used to
    implement the non-blocking popup windows. They're also found in some Demo Programs, so look there for proper use.

    :param button_text:      text in the button
    :type button_text:       (str)
    :param image_filename:   image filename if there is a button image
    :type image_filename:    image filename if there is a button image
    :param image_data:       in-RAM image to be displayed on button
    :type image_data:        in-RAM image to be displayed on button
    :param image_size:       image size (O.K.)
    :type image_size:        (Default = (None))
    :param image_subsample:  amount to reduce the size of the image
    :type image_subsample:   amount to reduce the size of the image
    :param border_width:     width of border around element
    :type border_width:      (int)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            if focus should be set to this
    :type focus:             (bool)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         Anything you want to store along with this button
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    return Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_CLOSES_WIN_ONLY,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )


# -------------------------  Calendar Chooser Button lazy function  ------------------------- #
def CalendarButton(
    button_text,
    target=(ThisRow, -1),
    close_when_date_chosen=True,
    default_date_m_d_y=(None, None, None),
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    tooltip=None,
    border_width=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    font=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    enable_events=None,
    key=None,
    k=None,
    visible=True,
    locale=None,
    format='%Y-%m-%d %H:%M:%S',
    begin_at_sunday_plus=0,
    month_names=None,
    day_abbreviations=None,
    title='Choose Date',
    no_titlebar=True,
    location=(None, None),
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """
    Button that will show a calendar chooser window.  Fills in the target element with result

    :param button_text:            text in the button
    :type button_text:             (str)
    :param target:                 Key or "coordinate" (see docs) of target element
    :type target:                  (int, int) | Any
    :param close_when_date_chosen: (Default = True)
    :type close_when_date_chosen:  bool
    :param default_date_m_d_y:     Beginning date to show
    :type default_date_m_d_y:      (int, int or None, int)
    :param image_filename:         image filename if there is a button image
    :type image_filename:          image filename if there is a button image
    :param image_data:             in-RAM image to be displayed on button
    :type image_data:              in-RAM image to be displayed on button
    :param image_size:             image size (O.K.)
    :type image_size:              (Default = (None))
    :param image_subsample:        amount to reduce the size of the image
    :type image_subsample:         amount to reduce the size of the image
    :param tooltip:                text, that will appear when mouse hovers over the element
    :type tooltip:                 (str)
    :param border_width:           width of border around element
    :type border_width:            width of border around element
    :param size:                   (w,h) w=characters-wide, h=rows-high
    :type size:                    (int, int)
    :param s:                      Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                       (int, int)  | (None, None) | int
    :param auto_size_button:       True if button size is determined by button text
    :type auto_size_button:        (bool)
    :param button_color:           button color (foreground, background)
    :type button_color:            (str, str) | str
    :param disabled:               set disable state for element (Default = False)
    :type disabled:                (bool)
    :param font:                   specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                    (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:         bool
    :param focus:                  if focus should be set to this
    :type focus:                   bool
    :param pad:                    Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:                     (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                      Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                       (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:                    key for uniquely identify this element (for window.find_element)
    :type key:                     str | int | tuple | object
    :param k:                      Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                       str | int | tuple | object
    :param locale:                 defines the locale used to get day names
    :type locale:                  str
    :param format:                 formats result using this strftime format
    :type format:                  str
    :param begin_at_sunday_plus:   Determines the left-most day in the display. 0=sunday, 1=monday, etc
    :type begin_at_sunday_plus:    (int)
    :param month_names:            optional list of month names to use (should be 12 items)
    :type month_names:             List[str]
    :param day_abbreviations:      optional list of abbreviations to display as the day of week
    :type day_abbreviations:       List[str]
    :param title:                  Title shown on the date chooser window
    :type title:                   (str)
    :param no_titlebar:            if True no titlebar will be shown on the date chooser window
    :type no_titlebar:             bool
    :param location:               Location on the screen (x,y) to show the calendar popup window
    :type location:                (int, int)
    :param visible:                set initial visibility state of the Button
    :type visible:                 (bool)
    :param metadata:               Anything you want to store along with this button
    :type metadata:                (Any)
    :param expand_x:               If True Element will expand in the Horizontal directions
    :type expand_x:                (bool)
    :param expand_y:               If True Element will expand in the Vertical directions
    :type expand_y:                (bool)
    :return:                       returns a button
    :rtype:                        (Button)
    """
    button = Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_CALENDAR_CHOOSER,
        target=target,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        enable_events=enable_events,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )
    button.calendar_close_when_chosen = close_when_date_chosen
    button.calendar_default_date_M_D_Y = default_date_m_d_y
    button.calendar_locale = locale
    button.calendar_format = format
    button.calendar_no_titlebar = no_titlebar
    button.calendar_location = location
    button.calendar_begin_at_sunday_plus = begin_at_sunday_plus
    button.calendar_month_names = month_names
    button.calendar_day_abbreviations = day_abbreviations
    button.calendar_title = title

    return button


# -------------------------  Calendar Chooser Button lazy function  ------------------------- #
def ColorChooserButton(
    button_text,
    target=(ThisRow, -1),
    image_filename=None,
    image_data=None,
    image_size=(None, None),
    image_subsample=None,
    tooltip=None,
    border_width=None,
    size=(None, None),
    s=(None, None),
    auto_size_button=None,
    button_color=None,
    disabled=False,
    font=None,
    bind_return_key=False,
    focus=False,
    pad=None,
    p=None,
    key=None,
    k=None,
    default_color=None,
    visible=True,
    metadata=None,
    expand_x=False,
    expand_y=False,
):
    """

    :param button_text:      text in the button
    :type button_text:       (str)
    :param target:           key or (row,col) target for the button. Note that -1 for column means 1 element to the left of this one. The constant ThisRow is used to indicate the current row. The Button itself is a valid target for some types of button
    :type target:            str | (int, int)
    :type image_filename:    (str)
    :param image_filename:   image filename if there is a button image. GIFs and PNGs only.
    :type image_filename:    (str)
    :param image_data:       Raw or Base64 representation of the image to put on button. Choose either filename or data
    :type image_data:        bytes | str
    :param image_size:       Size of the image in pixels (width, height)
    :type image_size:        (int, int)
    :param image_subsample:  amount to reduce the size of the image. Divides the size by this number. 2=1/2, 3=1/3, 4=1/4, etc
    :type image_subsample:   (int)
    :param tooltip:          text, that will appear when mouse hovers over the element
    :type tooltip:           (str)
    :param border_width:     width of border around element
    :type border_width:      (int)
    :param size:             (w,h) w=characters-wide, h=rows-high
    :type size:              (int, int)
    :param s:                Same as size parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
    :type s:                 (int, int)  | (None, None) | int
    :param auto_size_button: True if button size is determined by button text
    :type auto_size_button:  (bool)
    :param button_color:     button color (foreground, background)
    :type button_color:      (str, str) | str
    :param disabled:         set disable state for element (Default = False)
    :type disabled:          (bool)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:              (str or (str, int[, str]) or None)
    :param bind_return_key:  (Default = False) If True, this button will appear to be clicked when return key is pressed in other elements such as Input and elements with return key options
    :type bind_return_key:   (bool)
    :param focus:            Determines if initial focus should go to this element.
    :type focus:             (bool)
    :param pad:              Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
    :type pad:               (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param p:                Same as pad parameter.  It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
    :type p:                 (int, int) or ((int, int),(int,int)) or (int,(int,int)) or  ((int, int),int) | int
    :param key:              key for uniquely identify this element (for window.find_element)
    :type key:               str | int | tuple | object
    :param k:                Same as the Key. You can use either k or key. Which ever is set will be used.
    :type k:                 str | int | tuple | object
    :param default_color:    Color to be sent to tkinter to use as the default color
    :type default_color:     str
    :param visible:          set initial visibility state of the Button
    :type visible:           (bool)
    :param metadata:         User metadata that can be set to ANYTHING
    :type metadata:          (Any)
    :param expand_x:         If True Element will expand in the Horizontal directions
    :type expand_x:          (bool)
    :param expand_y:         If True Element will expand in the Vertical directions
    :type expand_y:          (bool)
    :return:                 returns a button
    :rtype:                  (Button)
    """
    button = Button(
        button_text=button_text,
        button_type=BUTTON_TYPE_COLOR_CHOOSER,
        target=target,
        image_filename=image_filename,
        image_data=image_data,
        image_size=image_size,
        image_subsample=image_subsample,
        border_width=border_width,
        tooltip=tooltip,
        size=size,
        s=s,
        auto_size_button=auto_size_button,
        button_color=button_color,
        font=font,
        disabled=disabled,
        bind_return_key=bind_return_key,
        focus=focus,
        pad=pad,
        p=p,
        key=key,
        k=k,
        visible=visible,
        metadata=metadata,
        expand_x=expand_x,
        expand_y=expand_y,
    )
    button.default_color = default_color
    return button


#####################################  -----  RESULTS   ------ ##################################################


def AddToReturnDictionary(form, element, value):
    form.ReturnValuesDictionary[element.Key] = value


def AddToReturnList(form, value):
    form.ReturnValuesList.append(value)


# ----------------------------------------------------------------------------#
# -------  FUNCTION InitializeResults.  Sets up form results matrix  --------#
def InitializeResults(form):
    _BuildResults(form, True, form)
    return


# =====  Radio Button RadVar encoding and decoding =====#
# =====  The value is simply the row * 1000 + col  =====#
def DecodeRadioRowCol(RadValue):
    container = RadValue // 100000
    row = RadValue // 1000
    col = RadValue % 1000
    return container, row, col


def EncodeRadioRowCol(container, row, col):
    RadValue = container * 100000 + row * 1000 + col
    return RadValue


# -------  FUNCTION BuildResults.  Form exiting so build the results to pass back  ------- #
# format of return values is
# (Button Pressed, input_values)
def _BuildResults(form, initialize_only, top_level_form):
    # Results for elements are:
    #   TEXT - Nothing
    #   INPUT - Read value from TK
    #   Button - Button Text and position as a Tuple

    # Get the initialized results so we don't have to rebuild
    form.ReturnValuesDictionary = {}
    form.ReturnValuesList = []
    _BuildResultsForSubform(form, initialize_only, top_level_form)
    if not top_level_form.LastButtonClickedWasRealtime:
        top_level_form.LastButtonClicked = None
    return form.ReturnValues


def _BuildResultsForSubform(form, initialize_only, top_level_form):
    event = top_level_form.LastButtonClicked
    for row_num, row in enumerate(form.Rows):
        for col_num, element in enumerate(row):
            if element.Key is not None and WRITE_ONLY_KEY in str(element.Key):
                continue
            value = None
            if element.Type == ELEM_TYPE_COLUMN:
                element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
                element.ReturnValuesList = []
                element.ReturnValuesDictionary = {}
                _BuildResultsForSubform(element, initialize_only, top_level_form)
                for item in element.ReturnValuesList:
                    AddToReturnList(top_level_form, item)
                if element.UseDictionary:
                    top_level_form.UseDictionary = True
                if element.ReturnValues[0] is not None:  # if a button was clicked
                    event = element.ReturnValues[0]

            if element.Type == ELEM_TYPE_FRAME:
                element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
                element.ReturnValuesList = []
                element.ReturnValuesDictionary = {}
                _BuildResultsForSubform(element, initialize_only, top_level_form)
                for item in element.ReturnValuesList:
                    AddToReturnList(top_level_form, item)
                if element.UseDictionary:
                    top_level_form.UseDictionary = True
                if element.ReturnValues[0] is not None:  # if a button was clicked
                    event = element.ReturnValues[0]

            if element.Type == ELEM_TYPE_PANE:
                element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
                element.ReturnValuesList = []
                element.ReturnValuesDictionary = {}
                _BuildResultsForSubform(element, initialize_only, top_level_form)
                for item in element.ReturnValuesList:
                    AddToReturnList(top_level_form, item)
                if element.UseDictionary:
                    top_level_form.UseDictionary = True
                if element.ReturnValues[0] is not None:  # if a button was clicked
                    event = element.ReturnValues[0]

            if element.Type == ELEM_TYPE_TAB_GROUP:
                element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
                element.ReturnValuesList = []
                element.ReturnValuesDictionary = {}
                _BuildResultsForSubform(element, initialize_only, top_level_form)
                for item in element.ReturnValuesList:
                    AddToReturnList(top_level_form, item)
                if element.UseDictionary:
                    top_level_form.UseDictionary = True
                if element.ReturnValues[0] is not None:  # if a button was clicked
                    event = element.ReturnValues[0]

            if element.Type == ELEM_TYPE_TAB:
                element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
                element.ReturnValuesList = []
                element.ReturnValuesDictionary = {}
                _BuildResultsForSubform(element, initialize_only, top_level_form)
                for item in element.ReturnValuesList:
                    AddToReturnList(top_level_form, item)
                if element.UseDictionary:
                    top_level_form.UseDictionary = True
                if element.ReturnValues[0] is not None:  # if a button was clicked
                    event = element.ReturnValues[0]

            if not initialize_only:
                if element.Type == ELEM_TYPE_INPUT_TEXT:
                    try:
                        value = element.TKStringVar.get()
                    except:
                        value = ''
                    if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents:
                        element.TKStringVar.set('')
                elif element.Type == ELEM_TYPE_INPUT_CHECKBOX:
                    value = element.TKIntVar.get()
                    value = value != 0
                elif element.Type == ELEM_TYPE_INPUT_RADIO:
                    RadVar = element.TKIntVar.get()
                    this_rowcol = EncodeRadioRowCol(form.ContainerElemementNumber, row_num, col_num)
                    # this_rowcol = element.EncodedRadioValue       # could use the saved one
                    value = RadVar == this_rowcol
                elif element.Type == ELEM_TYPE_BUTTON:
                    if top_level_form.LastButtonClicked == element.Key:
                        event = top_level_form.LastButtonClicked
                        if element.BType != BUTTON_TYPE_REALTIME:  # Do not clear realtime buttons
                            top_level_form.LastButtonClicked = None
                    if element.BType == BUTTON_TYPE_CALENDAR_CHOOSER:
                        # value = None
                        value = element.calendar_selection
                    else:
                        try:
                            value = element.TKStringVar.get()
                        except:
                            value = None
                elif element.Type == ELEM_TYPE_INPUT_COMBO:
                    element = element  # type: Combo
                    try:
                        if element.TKCombo.current() == -1:  # if the current value was not in the original list
                            value = element.TKCombo.get()
                        else:
                            value = element.Values[element.TKCombo.current()]  # get value from original list given index
                    except:
                        value = '*Exception occurred*'
                elif element.Type == ELEM_TYPE_INPUT_OPTION_MENU:
                    value = element.TKStringVar.get()
                elif element.Type == ELEM_TYPE_INPUT_LISTBOX:
                    try:
                        items = element.TKListbox.curselection()
                        value = [element.Values[int(item)] for item in items]
                    except Exception:
                        value = ''
                elif element.Type == ELEM_TYPE_INPUT_SPIN:
                    try:
                        value = element.TKStringVar.get()
                        for v in element.Values:
                            if str(v) == value:
                                value = v
                                break
                    except:
                        value = 0
                elif element.Type == ELEM_TYPE_INPUT_SLIDER:
                    try:
                        value = float(element.TKScale.get())
                    except:
                        value = 0
                elif element.Type == ELEM_TYPE_INPUT_MULTILINE:
                    if element.WriteOnly:  # if marked as "write only" when created, then don't include with the values being returned
                        continue
                    try:
                        value = element.TKText.get(1.0, tk.END)
                        if element.rstrip:
                            value = value.rstrip()
                        if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents:
                            element.TKText.delete('1.0', tk.END)
                    except:
                        value = None
                elif element.Type == ELEM_TYPE_TAB_GROUP:
                    try:
                        value = element.TKNotebook.tab(element.TKNotebook.index('current'))['text']
                        tab_key = element.find_currently_active_tab_key()
                        # tab_key = element.FindKeyFromTabName(value)
                        if tab_key is not None:
                            value = tab_key
                    except:
                        value = None
                elif element.Type == ELEM_TYPE_TABLE:
                    value = element.SelectedRows
                elif element.Type == ELEM_TYPE_TREE:
                    value = element.SelectedRows
                elif element.Type == ELEM_TYPE_GRAPH:
                    value = element.ClickPosition
                elif element.Type == ELEM_TYPE_MENUBAR:
                    if element.MenuItemChosen is not None:
                        event = top_level_form.LastButtonClicked = element.MenuItemChosen
                    value = element.MenuItemChosen
                    element.MenuItemChosen = None
                elif element.Type == ELEM_TYPE_BUTTONMENU:
                    element = element  # type: ButtonMenu
                    value = element.MenuItemChosen
                    if element.part_of_custom_menubar:
                        if element.MenuItemChosen is not None:
                            value = event = element.MenuItemChosen
                            top_level_form.LastButtonClicked = element.MenuItemChosen
                            if element.custom_menubar_key is not None:
                                top_level_form.ReturnValuesDictionary[element.custom_menubar_key] = value
                            element.MenuItemChosen = None
                        else:
                            if element.custom_menubar_key not in top_level_form.ReturnValuesDictionary:
                                top_level_form.ReturnValuesDictionary[element.custom_menubar_key] = None
                            value = None

            else:
                value = None

            # if an input type element, update the results
            if element.Type not in (
                ELEM_TYPE_BUTTON,
                ELEM_TYPE_TEXT,
                ELEM_TYPE_IMAGE,
                ELEM_TYPE_OUTPUT,
                ELEM_TYPE_PROGRESS_BAR,
                ELEM_TYPE_COLUMN,
                ELEM_TYPE_FRAME,
                ELEM_TYPE_SEPARATOR,
                ELEM_TYPE_TAB,
            ):
                if not (element.Type == ELEM_TYPE_BUTTONMENU and element.part_of_custom_menubar):
                    AddToReturnList(form, value)
                    AddToReturnDictionary(top_level_form, element, value)
            elif (element.Type == ELEM_TYPE_BUTTON and element.BType == BUTTON_TYPE_COLOR_CHOOSER and element.Target == (None, None)) or (
                element.Type == ELEM_TYPE_BUTTON
                and element.Key is not None
                and (
                    element.BType
                    in (
                        BUTTON_TYPE_SAVEAS_FILE,
                        BUTTON_TYPE_BROWSE_FILE,
                        BUTTON_TYPE_BROWSE_FILES,
                        BUTTON_TYPE_BROWSE_FOLDER,
                        BUTTON_TYPE_CALENDAR_CHOOSER,
                    )
                )
            ):
                AddToReturnList(form, value)
                AddToReturnDictionary(top_level_form, element, value)

    # if this is a column, then will fail so need to wrap with try
    try:
        if form.ReturnKeyboardEvents and form.LastKeyboardEvent is not None:
            event = form.LastKeyboardEvent
            form.LastKeyboardEvent = None
    except:
        pass

    try:
        form.ReturnValuesDictionary.pop(None, None)  # clean up dictionary include None was included
    except:
        pass

    # if no event was found
    if not initialize_only and event is None and form == top_level_form:
        queued_event_value = form._queued_thread_event_read()
        if queued_event_value is not None:
            event, value = queued_event_value
            AddToReturnList(form, value)
            form.ReturnValuesDictionary[event] = value

    if not form.UseDictionary:
        form.ReturnValues = event, form.ReturnValuesList
    else:
        form.ReturnValues = event, form.ReturnValuesDictionary

    return form.ReturnValues


def fill_form_with_values(window, values_dict):
    """
    Fills a window with values provided in a values dictionary { element_key : new_value }

    :param window:      The window object to fill
    :type window:       (Window)
    :param values_dict: A dictionary with element keys as key and value is values parm for Update call
    :type values_dict:  (Dict[Any, Any])
    :return:            None
    :rtype:             None
    """

    for element_key in values_dict:
        try:
            window.AllKeysDict[element_key].Update(values_dict[element_key])
        except Exception:
            print(f'Problem filling form. Perhaps bad key?  This is a suspected bad key: {element_key}')


def _FindElementWithFocusInSubForm(form):
    """
    Searches through a "sub-form" (can be a window or container) for the current element with focus

    :param form: a Window, Column, Frame, or TabGroup (container elements)
    :type form:  container elements
    :return:     Element
    :rtype:      Element | None
    """
    for row_num, row in enumerate(form.Rows):
        for col_num, element in enumerate(row):
            if element.Type == ELEM_TYPE_COLUMN:
                matching_elem = _FindElementWithFocusInSubForm(element)
                if matching_elem is not None:
                    return matching_elem
            elif element.Type == ELEM_TYPE_FRAME:
                matching_elem = _FindElementWithFocusInSubForm(element)
                if matching_elem is not None:
                    return matching_elem
            elif element.Type == ELEM_TYPE_TAB_GROUP:
                matching_elem = _FindElementWithFocusInSubForm(element)
                if matching_elem is not None:
                    return matching_elem
            elif element.Type == ELEM_TYPE_TAB:
                matching_elem = _FindElementWithFocusInSubForm(element)
                if matching_elem is not None:
                    return matching_elem
            elif element.Type == ELEM_TYPE_PANE:
                matching_elem = _FindElementWithFocusInSubForm(element)
                if matching_elem is not None:
                    return matching_elem
            elif element.Type == ELEM_TYPE_INPUT_TEXT:
                if element.TKEntry is not None:
                    if element.TKEntry is element.TKEntry.focus_get():
                        return element
            elif element.Type == ELEM_TYPE_INPUT_MULTILINE:
                if element.TKText is not None:
                    if element.TKText is element.TKText.focus_get():
                        return element
            elif element.Type == ELEM_TYPE_BUTTON:
                if element.TKButton is not None:
                    if element.TKButton is element.TKButton.focus_get():
                        return element
            else:  # The "Catch All" - if type isn't one of the above, try generic element.Widget
                try:
                    if element.Widget is not None:
                        if element.Widget is element.Widget.focus_get():
                            return element
                except:
                    return None

    return None


# 888    888      d8b          888
# 888    888      Y8P          888
# 888    888                   888
# 888888 888  888 888 88888b.  888888  .d88b.  888d888
# 888    888 .88P 888 888 "88b 888    d8P  Y8b 888P"
# 888    888888K  888 888  888 888    88888888 888
# Y88b.  888 "88b 888 888  888 Y88b.  Y8b.     888
#  "Y888 888  888 888 888  888  "Y888  "Y8888  888

# My crappy tkinter code starts here.  (search for "crappy" to get here quickly... that's the purpose if you hadn't caught on

"""
         )
        (
          ,
       ___)\
      (_____)
      (_______)

"""


# Chr0nic || This is probably *very* bad practice. But it works. Simple, but it works...
class VarHolder:
    canvas_holder = None

    def __init__(self):
        self.canvas_holder = None


# Also, to get to the point in the code where each element's widget is created, look for element + "p lacement" (without the space)


# ========================   TK CODE STARTS HERE ========================================= #
def _fixed_map(style, style_name, option, highlight_colors=(None, None)):
    # Fix for setting text colour for Tkinter 8.6.9
    # From: https://core.tcl.tk/tk/info/509cafafae

    # default_map = [elm for elm in style.map("Treeview", query_opt=option) if '!' not in elm[0]]
    # custom_map = [elm for elm in style.map(style_name, query_opt=option) if '!' not in elm[0]]
    default_map = [elm for elm in style.map('Treeview', query_opt=option) if '!' not in elm[0] and 'selected' not in elm[0]]
    custom_map = [elm for elm in style.map(style_name, query_opt=option) if '!' not in elm[0] and 'selected' not in elm[0]]
    if option == 'background':
        custom_map.append(
            (
                'selected',
                (highlight_colors[1] if highlight_colors[1] is not None else ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS[1]),
            )
        )
    elif option == 'foreground':
        custom_map.append(
            (
                'selected',
                (highlight_colors[0] if highlight_colors[0] is not None else ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS[0]),
            )
        )

    new_map = custom_map + default_map
    return new_map


def _add_right_click_menu(element, toplevel_form):
    if element.RightClickMenu == MENU_RIGHT_CLICK_DISABLED:
        return
    if element.RightClickMenu or toplevel_form.RightClickMenu:
        menu = element.RightClickMenu or toplevel_form.RightClickMenu
        top_menu = tk.Menu(
            toplevel_form.TKroot,
            tearoff=toplevel_form.right_click_menu_tearoff,
            tearoffcommand=element._tearoff_menu_callback,
        )

        if toplevel_form.right_click_menu_background_color not in (COLOR_SYSTEM_DEFAULT, None):
            top_menu.config(bg=toplevel_form.right_click_menu_background_color)
        if toplevel_form.right_click_menu_text_color not in (COLOR_SYSTEM_DEFAULT, None):
            top_menu.config(fg=toplevel_form.right_click_menu_text_color)
        if toplevel_form.right_click_menu_disabled_text_color not in (COLOR_SYSTEM_DEFAULT, None):
            top_menu.config(disabledforeground=toplevel_form.right_click_menu_disabled_text_color)
        if toplevel_form.right_click_menu_font is not None:
            top_menu.config(font=toplevel_form.right_click_menu_font)

        if toplevel_form.right_click_menu_selected_colors[0] not in (COLOR_SYSTEM_DEFAULT, None):
            top_menu.config(activeforeground=toplevel_form.right_click_menu_selected_colors[0])
        if toplevel_form.right_click_menu_selected_colors[1] not in (COLOR_SYSTEM_DEFAULT, None):
            top_menu.config(activebackground=toplevel_form.right_click_menu_selected_colors[1])
        AddMenuItem(top_menu, menu[1], element, right_click_menu=True)
        element.TKRightClickMenu = top_menu
        if running_mac():
            element.Widget.bind('<ButtonRelease-2>', element._RightClickMenuCallback)
        else:
            element.Widget.bind('<ButtonRelease-3>', element._RightClickMenuCallback)


def _change_ttk_theme(style, theme_name):
    global ttk_theme_in_use
    if theme_name not in style.theme_names():
        _error_popup_with_traceback(
            f'You are trying to use TTK theme "{theme_name}"',
            'This is not legal for your system',
            'The valid themes to choose from are: {}'.format(', '.join(style.theme_names())),
        )
        return False

    style.theme_use(theme_name)
    ttk_theme_in_use = theme_name
    return True


def _make_ttk_style_name(base_style, element, primary_style=False):
    Window._counter_for_ttk_widgets += 1
    style_name = str(Window._counter_for_ttk_widgets) + '___' + str(element.Key) + base_style
    if primary_style:
        element.ttk_style_name = style_name
    return style_name


def _make_ttk_scrollbar(element, orientation, window):
    """
    Creates a ttk scrollbar for elements as they are being added to the layout

    :param element:     The element
    :type element:      (Element)
    :param orientation: The orientation vertical ('v') or horizontal ('h')
    :type orientation:  (str)
    :param window:      The window containing the scrollbar
    :type window:       (Window)
    """

    style = ttk.Style()
    _change_ttk_theme(style, window.TtkTheme)
    if orientation[0].lower() == 'v':
        orient = 'vertical'
        style_name = _make_ttk_style_name('.Vertical.TScrollbar', element)
        # style_name_thumb = _make_ttk_style_name('.Vertical.TScrollbar.thumb', element)
        element.vsb_style = style
        element.vsb = ttk.Scrollbar(element.element_frame, orient=orient, command=element.Widget.yview, style=style_name)
        element.vsb_style_name = style_name
    else:
        orient = 'horizontal'
        style_name = _make_ttk_style_name('.Horizontal.TScrollbar', element)
        element.hsb_style = style
        element.hsb = ttk.Scrollbar(element.element_frame, orient=orient, command=element.Widget.xview, style=style_name)
        element.hsb_style_name = style_name

    # ------------------ Get the colors using heirarchy of element, window, options, settings ------------------
    # Trough Color
    if element.ttk_part_overrides.sbar_trough_color is not None:
        trough_color = element.ttk_part_overrides.sbar_trough_color
    elif window.ttk_part_overrides.sbar_trough_color is not None:
        trough_color = window.ttk_part_overrides.sbar_trough_color
    elif ttk_part_overrides_from_options.sbar_trough_color is not None:
        trough_color = ttk_part_overrides_from_options.sbar_trough_color
    else:
        trough_color = element.scroll_trough_color
    # Relief
    if element.ttk_part_overrides.sbar_relief is not None:
        scroll_relief = element.ttk_part_overrides.sbar_relief
    elif window.ttk_part_overrides.sbar_relief is not None:
        scroll_relief = window.ttk_part_overrides.sbar_relief
    elif ttk_part_overrides_from_options.sbar_relief is not None:
        scroll_relief = ttk_part_overrides_from_options.sbar_relief
    else:
        scroll_relief = element.scroll_relief
    # Frame Color
    if element.ttk_part_overrides.sbar_frame_color is not None:
        frame_color = element.ttk_part_overrides.sbar_frame_color
    elif window.ttk_part_overrides.sbar_frame_color is not None:
        frame_color = window.ttk_part_overrides.sbar_frame_color
    elif ttk_part_overrides_from_options.sbar_frame_color is not None:
        frame_color = ttk_part_overrides_from_options.sbar_frame_color
    else:
        frame_color = element.scroll_frame_color
    # Background Color
    if element.ttk_part_overrides.sbar_background_color is not None:
        background_color = element.ttk_part_overrides.sbar_background_color
    elif window.ttk_part_overrides.sbar_background_color is not None:
        background_color = window.ttk_part_overrides.sbar_background_color
    elif ttk_part_overrides_from_options.sbar_background_color is not None:
        background_color = ttk_part_overrides_from_options.sbar_background_color
    else:
        background_color = element.scroll_background_color
    # Arrow Color
    if element.ttk_part_overrides.sbar_arrow_color is not None:
        arrow_color = element.ttk_part_overrides.sbar_arrow_color
    elif window.ttk_part_overrides.sbar_arrow_color is not None:
        arrow_color = window.ttk_part_overrides.sbar_arrow_color
    elif ttk_part_overrides_from_options.sbar_arrow_color is not None:
        arrow_color = ttk_part_overrides_from_options.sbar_arrow_color
    else:
        arrow_color = element.scroll_arrow_color
    # Arrow Width
    if element.ttk_part_overrides.sbar_arrow_width is not None:
        arrow_width = element.ttk_part_overrides.sbar_arrow_width
    elif window.ttk_part_overrides.sbar_arrow_width is not None:
        arrow_width = window.ttk_part_overrides.sbar_arrow_width
    elif ttk_part_overrides_from_options.sbar_arrow_width is not None:
        arrow_width = ttk_part_overrides_from_options.sbar_arrow_width
    else:
        arrow_width = element.scroll_arrow_width
    # Scroll Width
    if element.ttk_part_overrides.sbar_width is not None:
        scroll_width = element.ttk_part_overrides.sbar_width
    elif window.ttk_part_overrides.sbar_width is not None:
        scroll_width = window.ttk_part_overrides.sbar_width
    elif ttk_part_overrides_from_options.sbar_width is not None:
        scroll_width = ttk_part_overrides_from_options.sbar_width
    else:
        scroll_width = element.scroll_width

    if trough_color not in (None, COLOR_SYSTEM_DEFAULT):
        style.configure(style_name, troughcolor=trough_color)

    if frame_color not in (None, COLOR_SYSTEM_DEFAULT):
        style.configure(style_name, framecolor=frame_color)
    if frame_color not in (None, COLOR_SYSTEM_DEFAULT):
        style.configure(style_name, bordercolor=frame_color)

    if (background_color not in (None, COLOR_SYSTEM_DEFAULT)) and (arrow_color not in (None, COLOR_SYSTEM_DEFAULT)):
        style.map(
            style_name,
            background=[
                ('selected', background_color),
                ('active', arrow_color),
                ('background', background_color),
                ('!focus', background_color),
            ],
        )
    if (background_color not in (None, COLOR_SYSTEM_DEFAULT)) and (arrow_color not in (None, COLOR_SYSTEM_DEFAULT)):
        style.map(
            style_name,
            arrowcolor=[
                ('selected', arrow_color),
                ('active', background_color),
                ('background', background_color),
                ('!focus', arrow_color),
            ],
        )

    if scroll_width not in (None, COLOR_SYSTEM_DEFAULT):
        style.configure(style_name, width=scroll_width)
    if arrow_width not in (None, COLOR_SYSTEM_DEFAULT):
        style.configure(style_name, arrowsize=arrow_width)

    if scroll_relief not in (None, COLOR_SYSTEM_DEFAULT):
        style.configure(style_name, relief=scroll_relief)


# @_timeit
def PackFormIntoFrame(form, containing_frame, toplevel_form):
    """

    :param form:             a window class
    :type form:              (Window)
    :param containing_frame: ???
    :type containing_frame:  ???
    :param toplevel_form:    ???
    :type toplevel_form:     (Window)

    """

    # Old bindings
    def yscroll_old(event):
        try:
            if event.num == 5 or event.delta < 0:
                VarHolder.canvas_holder.yview_scroll(1, 'unit')
            elif event.num == 4 or event.delta > 0:
                VarHolder.canvas_holder.yview_scroll(-1, 'unit')
        except:
            pass

    def xscroll_old(event):
        try:
            if event.num == 5 or event.delta < 0:
                VarHolder.canvas_holder.xview_scroll(1, 'unit')
            elif event.num == 4 or event.delta > 0:
                VarHolder.canvas_holder.xview_scroll(-1, 'unit')
        except:
            pass

    # Chr0nic
    def testMouseHook2(em):
        combo = em.TKCombo
        combo.unbind_class('TCombobox', '<MouseWheel>')
        combo.unbind_class('TCombobox', '<ButtonPress-4>')
        combo.unbind_class('TCombobox', '<ButtonPress-5>')
        containing_frame.unbind_all('<4>')
        containing_frame.unbind_all('<5>')
        containing_frame.unbind_all('<MouseWheel>')
        containing_frame.unbind_all('<Shift-MouseWheel>')

    # Chr0nic
    def testMouseUnhook2(em):
        containing_frame.bind_all('<4>', yscroll_old, add='+')
        containing_frame.bind_all('<5>', yscroll_old, add='+')
        containing_frame.bind_all('<MouseWheel>', yscroll_old, add='+')
        containing_frame.bind_all('<Shift-MouseWheel>', xscroll_old, add='+')

    # Chr0nic
    def testMouseHook(em):
        containing_frame.unbind_all('<4>')
        containing_frame.unbind_all('<5>')
        containing_frame.unbind_all('<MouseWheel>')
        containing_frame.unbind_all('<Shift-MouseWheel>')

    # Chr0nic
    def testMouseUnhook(em):
        containing_frame.bind_all('<4>', yscroll_old, add='+')
        containing_frame.bind_all('<5>', yscroll_old, add='+')
        containing_frame.bind_all('<MouseWheel>', yscroll_old, add='+')
        containing_frame.bind_all('<Shift-MouseWheel>', xscroll_old, add='+')

    def _char_width_in_pixels(font):
        return tkinter.font.Font(font=font).measure('A')  # single character width

    def _char_height_in_pixels(font):
        return tkinter.font.Font(font=font).metrics('linespace')

    def _string_width_in_pixels(font, string):
        return tkinter.font.Font(font=font).measure(string)  # single character width

    def _add_grab(element):
        try:
            if form.Grab is True or element.Grab is True:
                # if something already about to the button, then don't do the grab stuff
                if '<Button-1>' not in element.Widget.bind():
                    element.Widget.bind('<ButtonPress-1>', toplevel_form._StartMoveGrabAnywhere)
                    element.Widget.bind('<ButtonRelease-1>', toplevel_form._StopMove)
                    element.Widget.bind('<B1-Motion>', toplevel_form._OnMotionGrabAnywhere)
                element.ParentRowFrame.bind('<ButtonPress-1>', toplevel_form._StartMoveGrabAnywhere)
                element.ParentRowFrame.bind('<ButtonRelease-1>', toplevel_form._StopMove)
                element.ParentRowFrame.bind('<B1-Motion>', toplevel_form._OnMotionGrabAnywhere)
                if element.Type == ELEM_TYPE_COLUMN:
                    element.TKColFrame.canvas.bind('<ButtonPress-1>', toplevel_form._StartMoveGrabAnywhere)
                    element.TKColFrame.canvas.bind('<ButtonRelease-1>', toplevel_form._StopMove)
                    element.TKColFrame.canvas.bind('<B1-Motion>', toplevel_form._OnMotionGrabAnywhere)
        except Exception:
            pass
            # print(e)

    def _add_right_click_menu_and_grab(element):
        if element.RightClickMenu == MENU_RIGHT_CLICK_DISABLED:
            return
        if element.Type == ELEM_TYPE_TAB_GROUP:  # unless everything disabled, then need to always set a right click menu for tabgroups
            if toplevel_form.RightClickMenu == MENU_RIGHT_CLICK_DISABLED:
                return
            menu = _MENU_RIGHT_CLICK_TABGROUP_DEFAULT
        else:
            menu = element.RightClickMenu or form.RightClickMenu or toplevel_form.RightClickMenu

        if menu:
            top_menu = tk.Menu(
                toplevel_form.TKroot,
                tearoff=toplevel_form.right_click_menu_tearoff,
                tearoffcommand=element._tearoff_menu_callback,
            )

            if toplevel_form.right_click_menu_background_color not in (COLOR_SYSTEM_DEFAULT, None):
                top_menu.config(bg=toplevel_form.right_click_menu_background_color)
            if toplevel_form.right_click_menu_text_color not in (COLOR_SYSTEM_DEFAULT, None):
                top_menu.config(fg=toplevel_form.right_click_menu_text_color)
            if toplevel_form.right_click_menu_disabled_text_color not in (COLOR_SYSTEM_DEFAULT, None):
                top_menu.config(disabledforeground=toplevel_form.right_click_menu_disabled_text_color)
            if toplevel_form.right_click_menu_font is not None:
                top_menu.config(font=toplevel_form.right_click_menu_font)

            if toplevel_form.right_click_menu_selected_colors[0] not in (COLOR_SYSTEM_DEFAULT, None):
                top_menu.config(activeforeground=toplevel_form.right_click_menu_selected_colors[0])
            if toplevel_form.right_click_menu_selected_colors[1] not in (COLOR_SYSTEM_DEFAULT, None):
                top_menu.config(activebackground=toplevel_form.right_click_menu_selected_colors[1])
            AddMenuItem(top_menu, menu[1], element, right_click_menu=True)
            element.TKRightClickMenu = top_menu
            if toplevel_form.RightClickMenu:  # if the top level has a right click menu, then setup a callback for the Window itself
                if toplevel_form.TKRightClickMenu is None:
                    toplevel_form.TKRightClickMenu = top_menu
                    if running_mac():
                        toplevel_form.TKroot.bind('<ButtonRelease-2>', toplevel_form._RightClickMenuCallback)
                    else:
                        toplevel_form.TKroot.bind('<ButtonRelease-3>', toplevel_form._RightClickMenuCallback)
            if running_mac():
                element.Widget.bind('<ButtonRelease-2>', element._RightClickMenuCallback)
            else:
                element.Widget.bind('<ButtonRelease-3>', element._RightClickMenuCallback)
                try:
                    if element.Type == ELEM_TYPE_COLUMN:
                        element.TKColFrame.canvas.bind('<ButtonRelease-3>', element._RightClickMenuCallback)
                except:
                    pass
        _add_grab(element)

    def _add_expansion(element, row_should_expand, row_fill_direction):
        expand = True
        if element.expand_x and element.expand_y:
            fill = tk.BOTH
            row_fill_direction = tk.BOTH
            row_should_expand = True
        elif element.expand_x:
            fill = tk.X
            row_fill_direction = tk.X if row_fill_direction == tk.NONE else tk.BOTH if row_fill_direction == tk.Y else tk.X
        elif element.expand_y:
            fill = tk.Y
            row_fill_direction = tk.Y if row_fill_direction == tk.NONE else tk.BOTH if row_fill_direction == tk.X else tk.Y
            row_should_expand = True
        else:
            fill = tk.NONE
            expand = False
        return expand, fill, row_should_expand, row_fill_direction

    tclversion_detailed = tkinter.Tcl().eval('info patchlevel')

    # --------------------------------------------------------------------------- #
    # ****************  Use FlexForm to build the tkinter window ********** ----- #
    # Building is done row by row.                                                #
    # WARNING - You can't use print in this function. If the user has rerouted   #
    # stdout then there will be an error saying the window isn't finalized        #
    # --------------------------------------------------------------------------- #
    ######################### LOOP THROUGH ROWS #########################
    # *********** -------  Loop through ROWS  ------- ***********#
    for row_num, flex_row in enumerate(form.Rows):
        ######################### LOOP THROUGH ELEMENTS ON ROW #########################
        # *********** -------  Loop through ELEMENTS  ------- ***********#
        # *********** Make TK Row                             ***********#
        tk_row_frame = tk.Frame(containing_frame)
        row_should_expand = False
        row_fill_direction = tk.NONE

        if form.ElementJustification is not None:
            row_justify = form.ElementJustification
        else:
            row_justify = 'l'

        for col_num, element in enumerate(flex_row):
            element.ParentRowFrame = tk_row_frame
            element.element_frame = None  # for elements that have a scrollbar too
            element.ParentForm = toplevel_form  # save the button's parent form object
            if toplevel_form.Font and (element.Font == DEFAULT_FONT or element.Font is None):
                font = toplevel_form.Font
            elif element.Font is not None:
                font = element.Font
            else:
                font = DEFAULT_FONT
            # -------  Determine Auto-Size setting on a cascading basis ------- #
            if element.AutoSizeText is not None:  # if element overide
                auto_size_text = element.AutoSizeText
            elif toplevel_form.AutoSizeText is not None:  # if form override
                auto_size_text = toplevel_form.AutoSizeText
            else:
                auto_size_text = DEFAULT_AUTOSIZE_TEXT
            element_type = element.Type
            # Set foreground color
            text_color = element.TextColor
            elementpad = element.Pad if element.Pad is not None else toplevel_form.ElementPadding
            # element.pad_used = elementpad  # store the value used back into the element
            # Determine Element size
            element_size = element.Size
            if element_size == (None, None) and element_type not in (
                ELEM_TYPE_BUTTON,
                ELEM_TYPE_BUTTONMENU,
            ):  # user did not specify a size
                element_size = toplevel_form.DefaultElementSize
            elif element_size == (None, None) and element_type in (ELEM_TYPE_BUTTON, ELEM_TYPE_BUTTONMENU):
                element_size = toplevel_form.DefaultButtonElementSize
            else:
                auto_size_text = False  # if user has specified a size then it shouldn't autosize

            border_depth = toplevel_form.BorderDepth if toplevel_form.BorderDepth is not None else DEFAULT_BORDER_WIDTH
            try:
                if element.BorderWidth is not None:
                    border_depth = element.BorderWidth
            except:
                pass

            # -------------------------  COLUMN placement element  ------------------------- #
            if element_type == ELEM_TYPE_COLUMN:
                element = element  # type: Column
                # ----------------------- SCROLLABLE Column ----------------------
                if element.Scrollable:
                    element.Widget = element.TKColFrame = TkScrollableFrame(tk_row_frame, element.VerticalScrollOnly, element, toplevel_form)  # do not use yet!  not working
                    PackFormIntoFrame(element, element.TKColFrame.TKFrame, toplevel_form)
                    element.TKColFrame.TKFrame.update()
                    if element.Size == (None, None):  # if no size specified, use column width x column height/2
                        element.TKColFrame.canvas.config(
                            width=element.TKColFrame.TKFrame.winfo_reqwidth() // element.size_subsample_width,
                            height=element.TKColFrame.TKFrame.winfo_reqheight() // element.size_subsample_height,
                        )
                    else:
                        element.TKColFrame.canvas.config(
                            width=element.TKColFrame.TKFrame.winfo_reqwidth() // element.size_subsample_width,
                            height=element.TKColFrame.TKFrame.winfo_reqheight() // element.size_subsample_height,
                        )
                        if None not in (element.Size[0], element.Size[1]):
                            element.TKColFrame.canvas.config(width=element.Size[0], height=element.Size[1])
                        elif element.Size[1] is not None:
                            element.TKColFrame.canvas.config(height=element.Size[1])
                        elif element.Size[0] is not None:
                            element.TKColFrame.canvas.config(width=element.Size[0])
                    if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                        element.TKColFrame.canvas.config(background=element.BackgroundColor)
                        element.TKColFrame.TKFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0)
                        element.TKColFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0)
                # ----------------------- PLAIN Column ----------------------
                else:
                    if element.Size != (None, None):
                        element.Widget = element.TKColFrame = TkFixedFrame(tk_row_frame)
                        PackFormIntoFrame(element, element.TKColFrame.TKFrame, toplevel_form)
                        element.TKColFrame.TKFrame.update()
                        if None not in (element.Size[0], element.Size[1]):
                            element.TKColFrame.canvas.config(width=element.Size[0], height=element.Size[1])
                        elif element.Size[1] is not None:
                            element.TKColFrame.canvas.config(height=element.Size[1])
                        elif element.Size[0] is not None:
                            element.TKColFrame.canvas.config(width=element.Size[0])
                        if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                            element.TKColFrame.canvas.config(background=element.BackgroundColor)
                            element.TKColFrame.TKFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0)
                    else:
                        element.Widget = element.TKColFrame = tk.Frame(tk_row_frame)
                        PackFormIntoFrame(element, element.TKColFrame, toplevel_form)
                        if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                            element.TKColFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0)

                if element.Justification is None:
                    pass
                elif element.Justification.lower().startswith('l'):
                    row_justify = 'l'
                elif element.Justification.lower().startswith('c'):
                    row_justify = 'c'
                elif element.Justification.lower().startswith('r'):
                    row_justify = 'r'

                # anchor=tk.NW
                # side = tk.LEFT
                # row_justify = element.Justification

                # element.Widget = element.TKColFrame

                expand = True
                if element.expand_x and element.expand_y:
                    fill = tk.BOTH
                    row_fill_direction = tk.BOTH
                    row_should_expand = True
                elif element.expand_x:
                    fill = tk.X
                    row_fill_direction = tk.X
                elif element.expand_y:
                    fill = tk.Y
                    row_fill_direction = tk.Y
                    row_should_expand = True
                else:
                    fill = tk.NONE
                    expand = False

                if element.VerticalAlignment is not None:
                    anchor = tk.CENTER  # Default to center if a bad choice is made

                    if element.VerticalAlignment.lower().startswith('t'):
                        anchor = tk.N
                    if element.VerticalAlignment.lower().startswith('c'):
                        anchor = tk.CENTER
                    if element.VerticalAlignment.lower().startswith('b'):
                        anchor = tk.S
                    element.TKColFrame.pack(side=tk.LEFT, anchor=anchor, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                else:
                    element.TKColFrame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)

                # element.TKColFrame.pack(side=side, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both')
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKColFrame.pack_forget()

                _add_right_click_menu_and_grab(element)
                # if element.Grab:
                #     element._grab_anywhere_on()
                # row_should_expand = True
            # -------------------------  Pane placement element  ------------------------- #
            if element_type == ELEM_TYPE_PANE:
                bd = element.BorderDepth if element.BorderDepth is not None else border_depth
                element.PanedWindow = element.Widget = tk.PanedWindow(
                    tk_row_frame,
                    orient=tk.VERTICAL if element.Orientation.startswith('v') else tk.HORIZONTAL,
                    borderwidth=bd,
                    bd=bd,
                )
                if element.Relief is not None:
                    element.PanedWindow.configure(relief=element.Relief)
                element.PanedWindow.configure(handlesize=element.HandleSize)
                if element.ShowHandle:
                    element.PanedWindow.config(showhandle=True)
                if element.Size != (None, None):
                    element.PanedWindow.config(width=element.Size[0], height=element.Size[1])
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.PanedWindow.configure(background=element.BackgroundColor)
                for pane in element.PaneList:
                    pane.Widget = pane.TKColFrame = tk.Frame(element.PanedWindow)
                    pane.ParentPanedWindow = element.PanedWindow
                    PackFormIntoFrame(pane, pane.TKColFrame, toplevel_form)
                    if pane.visible:
                        element.PanedWindow.add(pane.TKColFrame)
                    if pane.BackgroundColor != COLOR_SYSTEM_DEFAULT and pane.BackgroundColor is not None:
                        pane.TKColFrame.configure(
                            background=pane.BackgroundColor,
                            highlightbackground=pane.BackgroundColor,
                            highlightcolor=pane.BackgroundColor,
                        )
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.PanedWindow.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                # element.PanedWindow.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both')
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.PanedWindow.pack_forget()
            # -------------------------  TEXT placement element  ------------------------- #
            elif element_type == ELEM_TYPE_TEXT:
                # auto_size_text = element.AutoSizeText
                element = element  # type: Text
                display_text = element.DisplayText  # text to display
                if auto_size_text is False:
                    width, height = element_size
                else:
                    width, height = None, None
                # ---===--- LABEL widget create and place --- #
                element = element  # type: Text
                bd = element.BorderWidth if element.BorderWidth is not None else border_depth
                stringvar = tk.StringVar()
                element.TKStringVar = stringvar
                stringvar.set(str(display_text))
                if auto_size_text:
                    width = 0
                if element.Justification is not None:
                    justification = element.Justification
                elif toplevel_form.TextJustification is not None:
                    justification = toplevel_form.TextJustification
                else:
                    justification = DEFAULT_TEXT_JUSTIFICATION
                justify = tk.LEFT if justification.startswith('l') else tk.CENTER if justification.startswith('c') else tk.RIGHT
                anchor = tk.NW if justification.startswith('l') else tk.N if justification.startswith('c') else tk.NE
                tktext_label = element.Widget = tk.Label(tk_row_frame, textvariable=stringvar, width=width, height=height, justify=justify, bd=bd, font=font)
                # Set wrap-length for text (in PIXELS) == PAIN IN THE ASS
                wraplen = tktext_label.winfo_reqwidth()  # width of widget in Pixels
                if auto_size_text or (not auto_size_text and height == 1):  # if just 1 line high, ensure no wrap happens
                    wraplen = 0
                tktext_label.configure(anchor=anchor, wraplen=wraplen)  # set wrap to width of widget
                if element.Relief is not None:
                    tktext_label.configure(relief=element.Relief)
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    tktext_label.configure(background=element.BackgroundColor)
                if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
                    tktext_label.configure(fg=element.TextColor)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                tktext_label.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # tktext_label.pack_forget()
                element.TKText = tktext_label
                if element.ClickSubmits:
                    tktext_label.bind('<Button-1>', element._TextClickedHandler)
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)
                if element.Grab:
                    element._grab_anywhere_on()
            # -------------------------  BUTTON placement element non-ttk version  ------------------------- #
            elif (element_type == ELEM_TYPE_BUTTON and element.UseTtkButtons is False) or (element_type == ELEM_TYPE_BUTTON and element.UseTtkButtons is not True and toplevel_form.UseTtkButtons is not True):
                element = element  # type: Button
                element.UseTtkButtons = False  # indicate that ttk button was not used
                stringvar = tk.StringVar()
                element.TKStringVar = stringvar
                element.Location = (row_num, col_num)
                btext = element.ButtonText
                btype = element.BType
                if element.AutoSizeButton is not None:
                    auto_size = element.AutoSizeButton
                else:
                    auto_size = toplevel_form.AutoSizeButtons
                if auto_size is False or element.Size[0] is not None:
                    width, height = element_size
                else:
                    width = 0
                    height = toplevel_form.DefaultButtonElementSize[1]
                if element.ButtonColor != (None, None) and element.ButtonColor != DEFAULT_BUTTON_COLOR:
                    bc = element.ButtonColor
                elif toplevel_form.ButtonColor != (None, None) and toplevel_form.ButtonColor != DEFAULT_BUTTON_COLOR:
                    bc = toplevel_form.ButtonColor
                else:
                    bc = DEFAULT_BUTTON_COLOR

                bd = element.BorderWidth
                pos = -1
                if DEFAULT_USE_BUTTON_SHORTCUTS is True:
                    pos = btext.find(MENU_SHORTCUT_CHARACTER)
                    if pos != -1:
                        if pos < len(MENU_SHORTCUT_CHARACTER) or btext[pos - len(MENU_SHORTCUT_CHARACTER)] != '\\':
                            btext = btext[:pos] + btext[pos + len(MENU_SHORTCUT_CHARACTER) :]
                        else:
                            btext = btext.replace('\\' + MENU_SHORTCUT_CHARACTER, MENU_SHORTCUT_CHARACTER)
                            pos = -1
                tkbutton = element.Widget = tk.Button(tk_row_frame, text=btext, width=width, height=height, justify=tk.CENTER, bd=bd, font=font)
                if pos != -1:
                    tkbutton.config(underline=pos)
                try:
                    if btype != BUTTON_TYPE_REALTIME:
                        tkbutton.config(command=element.ButtonCallBack)

                    else:
                        tkbutton.bind('<ButtonRelease-1>', element.ButtonReleaseCallBack)
                        tkbutton.bind('<ButtonPress-1>', element.ButtonPressCallBack)
                    if bc != (None, None) and COLOR_SYSTEM_DEFAULT not in bc:
                        tkbutton.config(foreground=bc[0], background=bc[1])
                    else:
                        if bc[0] != COLOR_SYSTEM_DEFAULT:
                            tkbutton.config(foreground=bc[0])
                        if bc[1] != COLOR_SYSTEM_DEFAULT:
                            tkbutton.config(background=bc[1])
                except Exception as e:
                    _error_popup_with_traceback(
                        'Button has a problem....',
                        'The traceback information will not show the line in your layout with the problem, but it does tell you which window.',
                        f'Error {e}',
                        # 'Button Text: {}'.format(btext),
                        # 'Button key: {}'.format(element.Key),
                        # 'Color string: {}'.format(bc),
                        f"Parent Window's Title: {toplevel_form.Title}",
                    )

                if bd == 0 and not running_mac():
                    tkbutton.config(relief=tk.FLAT)

                element.TKButton = tkbutton  # not used yet but save the TK button in case
                if elementpad[0] == 0 or elementpad[1] == 0:
                    tkbutton.config(highlightthickness=0)

                ## -------------- TK Button With Image -------------- ##
                if element.ImageFilename:  # if button has an image on it
                    tkbutton.config(highlightthickness=0)
                    try:
                        photo = tk.PhotoImage(file=element.ImageFilename)
                        if element.ImageSubsample:
                            photo = photo.subsample(element.ImageSubsample)
                        if element.zoom:
                            photo = photo.zoom(element.zoom)
                        if element.ImageSize != (None, None):
                            width, height = element.ImageSize
                        else:
                            width, height = photo.width(), photo.height()
                    except Exception as e:
                        _error_popup_with_traceback(
                            f'Button Element error {e}',
                            f'Image filename: {element.ImageFilename}',
                            'NOTE - file format must be PNG or GIF!',
                            f'Button element key: {element.Key}',
                            f"Parent Window's Title: {toplevel_form.Title}",
                        )
                    tkbutton.config(image=photo, compound=tk.CENTER, width=width, height=height)
                    tkbutton.image = photo
                if element.ImageData:  # if button has an image on it
                    tkbutton.config(highlightthickness=0)
                    try:
                        photo = tk.PhotoImage(data=element.ImageData)
                        if element.ImageSubsample:
                            photo = photo.subsample(element.ImageSubsample)
                        if element.zoom:
                            photo = photo.zoom(element.zoom)
                        if element.ImageSize != (None, None):
                            width, height = element.ImageSize
                        else:
                            width, height = photo.width(), photo.height()
                        tkbutton.config(image=photo, compound=tk.CENTER, width=width, height=height)
                        tkbutton.image = photo
                    except Exception as e:
                        _error_popup_with_traceback(
                            f'Button Element error {e}',
                            'Problem using BASE64 Image data Image Susample',
                            f'Buton element key: {element.Key}',
                            f"Parent Window's Title: {toplevel_form.Title}",
                        )

                if width != 0:
                    wraplen = width * _char_width_in_pixels(font)
                    tkbutton.configure(wraplength=wraplen)  # set wrap to width of widget
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)

                tkbutton.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # tkbutton.pack_forget()
                if element.BindReturnKey:
                    element.TKButton.bind('<Return>', element._ReturnKeyHandler)
                if element.Focus is True or (toplevel_form.UseDefaultFocus and not toplevel_form.FocusSet):
                    toplevel_form.FocusSet = True
                    element.TKButton.bind('<Return>', element._ReturnKeyHandler)
                    element.TKButton.focus_set()
                    toplevel_form.TKroot.focus_force()
                if element.Disabled is True:
                    element.TKButton['state'] = 'disabled'
                if element.DisabledButtonColor != (None, None) and element.DisabledButtonColor != (
                    COLOR_SYSTEM_DEFAULT,
                    COLOR_SYSTEM_DEFAULT,
                ):
                    if element.DisabledButtonColor[0] not in (None, COLOR_SYSTEM_DEFAULT):
                        element.TKButton['disabledforeground'] = element.DisabledButtonColor[0]
                if element.MouseOverColors[1] not in (COLOR_SYSTEM_DEFAULT, None):
                    tkbutton.config(activebackground=element.MouseOverColors[1])
                if element.MouseOverColors[0] not in (COLOR_SYSTEM_DEFAULT, None):
                    tkbutton.config(activeforeground=element.MouseOverColors[0])

                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                try:
                    if element.HighlightColors[1] != COLOR_SYSTEM_DEFAULT:
                        tkbutton.config(highlightbackground=element.HighlightColors[1])
                    if element.HighlightColors[0] != COLOR_SYSTEM_DEFAULT:
                        tkbutton.config(highlightcolor=element.HighlightColors[0])
                except Exception as e:
                    _error_popup_with_traceback(
                        f'Button Element error {e}',
                        f'Button element key: {element.Key}',
                        f'Button text: {btext}',
                        f'Has a bad highlight color {element.HighlightColors}',
                        f"Parent Window's Title: {toplevel_form.Title}",
                    )
                    # print('Button with text: ', btext, 'has a bad highlight color', element.HighlightColors)
                _add_right_click_menu_and_grab(element)

            # -------------------------  BUTTON placement element ttk version ------------------------- #
            elif element_type == ELEM_TYPE_BUTTON:
                element = element  # type: Button
                element.UseTtkButtons = True  # indicate that ttk button was used
                stringvar = tk.StringVar()
                element.TKStringVar = stringvar
                element.Location = (row_num, col_num)
                btext = element.ButtonText
                pos = -1
                if DEFAULT_USE_BUTTON_SHORTCUTS is True:
                    pos = btext.find(MENU_SHORTCUT_CHARACTER)
                    if pos != -1:
                        if pos < len(MENU_SHORTCUT_CHARACTER) or btext[pos - len(MENU_SHORTCUT_CHARACTER)] != '\\':
                            btext = btext[:pos] + btext[pos + len(MENU_SHORTCUT_CHARACTER) :]
                        else:
                            btext = btext.replace('\\' + MENU_SHORTCUT_CHARACTER, MENU_SHORTCUT_CHARACTER)
                            pos = -1
                btype = element.BType
                if element.AutoSizeButton is not None:
                    auto_size = element.AutoSizeButton
                else:
                    auto_size = toplevel_form.AutoSizeButtons
                if auto_size is False or element.Size[0] is not None:
                    width, height = element_size
                else:
                    width = 0
                    height = toplevel_form.DefaultButtonElementSize[1]
                if element.ButtonColor != (None, None) and element.ButtonColor != COLOR_SYSTEM_DEFAULT:
                    bc = element.ButtonColor
                elif toplevel_form.ButtonColor != (None, None) and toplevel_form.ButtonColor != COLOR_SYSTEM_DEFAULT:
                    bc = toplevel_form.ButtonColor
                else:
                    bc = DEFAULT_BUTTON_COLOR
                bd = element.BorderWidth
                tkbutton = element.Widget = ttk.Button(tk_row_frame, text=btext, width=width)
                if pos != -1:
                    tkbutton.config(underline=pos)
                if btype != BUTTON_TYPE_REALTIME:
                    tkbutton.config(command=element.ButtonCallBack)
                else:
                    tkbutton.bind('<ButtonRelease-1>', element.ButtonReleaseCallBack)
                    tkbutton.bind('<ButtonPress-1>', element.ButtonPressCallBack)
                style_name = _make_ttk_style_name('.TButton', element, primary_style=True)
                button_style = ttk.Style()
                element.ttk_style = button_style
                _change_ttk_theme(button_style, toplevel_form.TtkTheme)
                button_style.configure(style_name, font=font)

                if bc != (None, None) and COLOR_SYSTEM_DEFAULT not in bc:
                    button_style.configure(style_name, foreground=bc[0], background=bc[1])
                elif bc[0] != COLOR_SYSTEM_DEFAULT:
                    button_style.configure(style_name, foreground=bc[0])
                elif bc[1] != COLOR_SYSTEM_DEFAULT:
                    button_style.configure(style_name, background=bc[1])

                if bd == 0 and not running_mac():
                    button_style.configure(style_name, relief=tk.FLAT)
                    button_style.configure(style_name, borderwidth=0)
                else:
                    button_style.configure(style_name, borderwidth=bd)
                button_style.configure(style_name, justify=tk.CENTER)

                if element.MouseOverColors[1] not in (COLOR_SYSTEM_DEFAULT, None):
                    button_style.map(style_name, background=[('active', element.MouseOverColors[1])])
                if element.MouseOverColors[0] not in (COLOR_SYSTEM_DEFAULT, None):
                    button_style.map(style_name, foreground=[('active', element.MouseOverColors[0])])

                if element.DisabledButtonColor[0] not in (COLOR_SYSTEM_DEFAULT, None):
                    button_style.map(style_name, foreground=[('disabled', element.DisabledButtonColor[0])])
                if element.DisabledButtonColor[1] not in (COLOR_SYSTEM_DEFAULT, None):
                    button_style.map(style_name, background=[('disabled', element.DisabledButtonColor[1])])

                if height > 1:
                    button_style.configure(style_name, padding=height * _char_height_in_pixels(font))  # should this be height instead?
                if width != 0:
                    wraplen = width * _char_width_in_pixels(font)  # width of widget in Pixels
                    button_style.configure(style_name, wraplength=wraplen)  # set wrap to width of widget

                ## -------------- TTK Button With Image -------------- ##
                if element.ImageFilename:  # if button has an image on it
                    button_style.configure(style_name, borderwidth=0)
                    # tkbutton.configure(highlightthickness=0)
                    photo = tk.PhotoImage(file=element.ImageFilename)
                    if element.ImageSubsample:
                        photo = photo.subsample(element.ImageSubsample)
                    if element.zoom:
                        photo = photo.zoom(element.zoom)
                    if element.ImageSize != (None, None):
                        width, height = element.ImageSize
                    else:
                        width, height = photo.width(), photo.height()
                    button_style.configure(style_name, image=photo, compound=tk.CENTER, width=width, height=height)
                    tkbutton.image = photo
                if element.ImageData:  # if button has an image on it
                    # tkbutton.configure(highlightthickness=0)
                    button_style.configure(style_name, borderwidth=0)

                    photo = tk.PhotoImage(data=element.ImageData)
                    if element.ImageSubsample:
                        photo = photo.subsample(element.ImageSubsample)
                    if element.zoom:
                        photo = photo.zoom(element.zoom)
                    if element.ImageSize != (None, None):
                        width, height = element.ImageSize
                    else:
                        width, height = photo.width(), photo.height()
                    button_style.configure(style_name, image=photo, compound=tk.CENTER, width=width, height=height)
                    # tkbutton.configure(image=photo, compound=tk.CENTER, width=width, height=height)
                    tkbutton.image = photo

                element.TKButton = tkbutton  # not used yet but save the TK button in case
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                tkbutton.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # tkbutton.pack_forget()
                if element.BindReturnKey:
                    element.TKButton.bind('<Return>', element._ReturnKeyHandler)
                if element.Focus is True or (toplevel_form.UseDefaultFocus and not toplevel_form.FocusSet):
                    toplevel_form.FocusSet = True
                    element.TKButton.bind('<Return>', element._ReturnKeyHandler)
                    element.TKButton.focus_set()
                    toplevel_form.TKroot.focus_force()
                if element.Disabled is True:
                    element.TKButton['state'] = 'disabled'

                tkbutton.configure(style=style_name)  # IMPORTANT!  Apply the style to the button!
                _add_right_click_menu_and_grab(element)

                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
            # -------------------------  BUTTONMENU placement element  ------------------------- #
            elif element_type == ELEM_TYPE_BUTTONMENU:
                element = element  # type: ButtonMenu
                element.Location = (row_num, col_num)
                btext = element.ButtonText
                if element.AutoSizeButton is not None:
                    auto_size = element.AutoSizeButton
                else:
                    auto_size = toplevel_form.AutoSizeButtons
                if auto_size is False or element.Size[0] is not None:
                    width, height = element_size
                else:
                    width = 0
                    height = toplevel_form.DefaultButtonElementSize[1]
                if element.ButtonColor != (None, None) and element.ButtonColor != DEFAULT_BUTTON_COLOR:
                    bc = element.ButtonColor
                elif toplevel_form.ButtonColor != (None, None) and toplevel_form.ButtonColor != DEFAULT_BUTTON_COLOR:
                    bc = toplevel_form.ButtonColor
                else:
                    bc = DEFAULT_BUTTON_COLOR
                bd = element.BorderWidth
                if element.ItemFont is None:
                    element.ItemFont = font
                tkbutton = element.Widget = tk.Menubutton(tk_row_frame, text=btext, width=width, height=height, justify=tk.LEFT, bd=bd, font=font)
                element.TKButtonMenu = tkbutton
                if bc != (None, None) and bc != COLOR_SYSTEM_DEFAULT and bc[1] != COLOR_SYSTEM_DEFAULT:
                    tkbutton.config(foreground=bc[0], background=bc[1])
                    tkbutton.config(activebackground=bc[0])
                    tkbutton.config(activeforeground=bc[1])
                elif bc[0] != COLOR_SYSTEM_DEFAULT:
                    tkbutton.config(foreground=bc[0])
                    tkbutton.config(activebackground=bc[0])
                if bd == 0 and not running_mac():
                    tkbutton.config(relief=RELIEF_FLAT)
                elif bd != 0:
                    tkbutton.config(relief=RELIEF_RAISED)

                element.TKButton = tkbutton  # not used yet but save the TK button in case
                wraplen = tkbutton.winfo_reqwidth()  # width of widget in Pixels
                if element.ImageFilename:  # if button has an image on it
                    photo = tk.PhotoImage(file=element.ImageFilename)
                    if element.ImageSubsample:
                        photo = photo.subsample(element.ImageSubsample)
                    if element.zoom:
                        photo = photo.zoom(element.zoom)
                    if element.ImageSize != (None, None):
                        width, height = element.ImageSize
                    else:
                        width, height = photo.width(), photo.height()
                    tkbutton.config(image=photo, compound=tk.CENTER, width=width, height=height)
                    tkbutton.image = photo
                if element.ImageData:  # if button has an image on it
                    photo = tk.PhotoImage(data=element.ImageData)
                    if element.ImageSubsample:
                        photo = photo.subsample(element.ImageSubsample)
                    if element.zoom:
                        photo = photo.zoom(element.zoom)
                    if element.ImageSize != (None, None):
                        width, height = element.ImageSize
                    else:
                        width, height = photo.width(), photo.height()
                    tkbutton.config(image=photo, compound=tk.CENTER, width=width, height=height)
                    tkbutton.image = photo
                if width != 0:
                    tkbutton.configure(wraplength=wraplen + 10)  # set wrap to width of widget
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                tkbutton.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)

                menu_def = element.MenuDefinition

                element.TKMenu = top_menu = tk.Menu(
                    tkbutton,
                    tearoff=element.Tearoff,
                    font=element.ItemFont,
                    tearoffcommand=element._tearoff_menu_callback,
                )

                if element.BackgroundColor not in (COLOR_SYSTEM_DEFAULT, None):
                    top_menu.config(bg=element.BackgroundColor)
                    top_menu.config(activeforeground=element.BackgroundColor)
                if element.TextColor not in (COLOR_SYSTEM_DEFAULT, None):
                    top_menu.config(fg=element.TextColor)
                    top_menu.config(activebackground=element.TextColor)
                if element.DisabledTextColor not in (COLOR_SYSTEM_DEFAULT, None):
                    top_menu.config(disabledforeground=element.DisabledTextColor)
                if element.ItemFont is not None:
                    top_menu.config(font=element.ItemFont)

                AddMenuItem(top_menu, menu_def[1], element)
                if elementpad[0] == 0 or elementpad[1] == 0:
                    tkbutton.config(highlightthickness=0)
                tkbutton.configure(menu=top_menu)
                element.TKMenu = top_menu
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # tkbutton.pack_forget()
                if element.Disabled:
                    element.TKButton['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)

            # -------------------------  INPUT placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_TEXT:
                element = element  # type: InputText
                default_text = element.DefaultText
                element.TKStringVar = tk.StringVar()
                element.TKStringVar.set(default_text)
                show = element.PasswordCharacter if element.PasswordCharacter else ''
                bd = border_depth
                if element.Justification is not None:
                    justification = element.Justification
                else:
                    justification = DEFAULT_TEXT_JUSTIFICATION
                justify = tk.LEFT if justification.startswith('l') else tk.CENTER if justification.startswith('c') else tk.RIGHT
                # anchor = tk.NW if justification == 'left' else tk.N if justification == 'center' else tk.NE
                element.TKEntry = element.Widget = tk.Entry(
                    tk_row_frame,
                    width=element_size[0],
                    textvariable=element.TKStringVar,
                    bd=bd,
                    font=font,
                    show=show,
                    justify=justify,
                )
                if element.ChangeSubmits:
                    element.TKEntry.bind('<Key>', element._KeyboardHandler)
                element.TKEntry.bind('<Return>', element._ReturnKeyHandler)

                if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKEntry.configure(background=element.BackgroundColor, selectforeground=element.BackgroundColor)

                if text_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKEntry.configure(fg=text_color, selectbackground=text_color)
                    element.TKEntry.config(insertbackground=text_color)
                if element.selected_background_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKEntry.configure(selectbackground=element.selected_background_color)
                if element.selected_text_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKEntry.configure(selectforeground=element.selected_text_color)
                if element.disabled_readonly_background_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKEntry.config(readonlybackground=element.disabled_readonly_background_color)
                if element.disabled_readonly_text_color not in (None, COLOR_SYSTEM_DEFAULT) and element.Disabled:
                    element.TKEntry.config(fg=element.disabled_readonly_text_color)

                element.Widget.config(highlightthickness=0)
                # element.pack_keywords = {'side':tk.LEFT, 'padx':elementpad[0], 'pady':elementpad[1], 'expand':False, 'fill':tk.NONE }
                # element.TKEntry.pack(**element.pack_keywords)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKEntry.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKEntry.pack_forget()
                if element.Focus is True or (toplevel_form.UseDefaultFocus and not toplevel_form.FocusSet):
                    toplevel_form.FocusSet = True
                    element.TKEntry.focus_set()
                if element.Disabled:
                    element.TKEntry['state'] = 'readonly' if element.UseReadonlyForDisable else 'disabled'
                if element.ReadOnly:
                    element.TKEntry['state'] = 'readonly'

                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKEntry, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

                # row_should_expand = True

            # -------------------------  COMBO placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_COMBO:
                element = element  # type: Combo
                max_line_len = max([len(str(line)) for line in element.Values]) if len(element.Values) else 0
                if auto_size_text is False:
                    width = element_size[0]
                else:
                    width = max_line_len + 1
                element.TKStringVar = tk.StringVar()
                style_name = _make_ttk_style_name('.TCombobox', element, primary_style=True)
                combostyle = ttk.Style()
                element.ttk_style = combostyle
                _change_ttk_theme(combostyle, toplevel_form.TtkTheme)

                # Creates a unique name for each field element(Sure there is a better way to do this)
                # unique_field = _make_ttk_style_name('.TCombobox.field', element)

                # Set individual widget options
                try:
                    if element.TextColor not in (None, COLOR_SYSTEM_DEFAULT):
                        combostyle.configure(style_name, foreground=element.TextColor)
                        combostyle.configure(style_name, selectbackground=element.TextColor)
                        combostyle.configure(style_name, insertcolor=element.TextColor)
                        combostyle.map(style_name, fieldforeground=[('readonly', element.TextColor)])
                    if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                        combostyle.configure(style_name, selectforeground=element.BackgroundColor)
                        combostyle.map(style_name, fieldbackground=[('readonly', element.BackgroundColor)])
                        combostyle.configure(style_name, fieldbackground=element.BackgroundColor)

                    if element.button_arrow_color not in (None, COLOR_SYSTEM_DEFAULT):
                        combostyle.configure(style_name, arrowcolor=element.button_arrow_color)
                    if element.button_background_color not in (None, COLOR_SYSTEM_DEFAULT):
                        combostyle.configure(style_name, background=element.button_background_color)
                    if element.Readonly is True:
                        if element.TextColor not in (None, COLOR_SYSTEM_DEFAULT):
                            combostyle.configure(style_name, selectforeground=element.TextColor)
                        if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                            combostyle.configure(style_name, selectbackground=element.BackgroundColor)

                except Exception as e:
                    _error_popup_with_traceback(
                        f'Combo Element error {e}',
                        f'Combo element key: {element.Key}',
                        'One of your colors is bad. Check the text, background, button background and button arrow colors',
                        f"Parent Window's Title: {toplevel_form.Title}",
                    )

                # Strange code that is needed to set the font for the drop-down list
                element._dropdown_newfont = tkinter.font.Font(font=font)
                tk_row_frame.option_add('*TCombobox*Listbox*Font', element._dropdown_newfont)

                element.TKCombo = element.Widget = ttk.Combobox(tk_row_frame, width=width, textvariable=element.TKStringVar, font=font, style=style_name)

                # make tcl call to deal with colors for the drop-down formatting
                try:
                    if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT) and element.TextColor not in (
                        None,
                        COLOR_SYSTEM_DEFAULT,
                    ):
                        element.Widget.tk.eval(
                            '[ttk::combobox::PopdownWindow {}].f.l configure -foreground {} -background {} -selectforeground {} -selectbackground {}'.format(
                                element.Widget,
                                element.TextColor,
                                element.BackgroundColor,
                                element.BackgroundColor,
                                element.TextColor,
                            )
                        )
                except Exception:
                    pass  # going to let this one slide

                # Chr0nic
                element.TKCombo.bind('<Enter>', lambda event, em=element: testMouseHook2(em))
                element.TKCombo.bind('<Leave>', lambda event, em=element: testMouseUnhook2(em))

                if toplevel_form.UseDefaultFocus and not toplevel_form.FocusSet:
                    toplevel_form.FocusSet = True
                    element.TKCombo.focus_set()

                if element.Size[1] != 1 and element.Size[1] is not None:
                    element.TKCombo.configure(height=element.Size[1])
                element.TKCombo['values'] = element.Values
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKCombo.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKCombo.pack_forget()
                if element.DefaultValue is not None:
                    element.TKCombo.set(element.DefaultValue)
                if element.ChangeSubmits:
                    element.TKCombo.bind('<<ComboboxSelected>>', element._ComboboxSelectHandler)
                if element.BindReturnKey:
                    element.TKCombo.bind('<Return>', element._ComboboxSelectHandler)
                if element.enable_per_char_events:
                    element.TKCombo.bind('<Key>', element._KeyboardHandler)
                if element.Readonly:
                    element.TKCombo['state'] = 'readonly'
                if element.Disabled is True:  # note overrides readonly if disabled
                    element.TKCombo['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

            # -------------------------  OPTIONMENU placement Element (Like ComboBox but different) element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_OPTION_MENU:
                max_line_len = max([len(str(line)) for line in element.Values])
                if auto_size_text is False:
                    width = element_size[0]
                else:
                    width = max_line_len
                element.TKStringVar = tk.StringVar()
                if element.DefaultValue:
                    element.TKStringVar.set(element.DefaultValue)
                element.TKOptionMenu = element.Widget = tk.OptionMenu(tk_row_frame, element.TKStringVar, *element.Values)
                element.TKOptionMenu.config(highlightthickness=0, font=font, width=width)
                element.TKOptionMenu['menu'].config(font=font)
                element.TKOptionMenu.config(borderwidth=border_depth)
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKOptionMenu.configure(background=element.BackgroundColor)
                    element.TKOptionMenu['menu'].config(background=element.BackgroundColor)
                if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
                    element.TKOptionMenu.configure(fg=element.TextColor)
                    element.TKOptionMenu['menu'].config(fg=element.TextColor)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKOptionMenu.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKOptionMenu.pack_forget()
                if element.Disabled is True:
                    element.TKOptionMenu['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKOptionMenu, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
            # -------------------------  LISTBOX placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_LISTBOX:
                element = element  # type: Listbox
                max_line_len = max([len(str(line)) for line in element.Values]) if len(element.Values) else 0
                if auto_size_text is False:
                    width = element_size[0]
                else:
                    width = max_line_len
                element_frame = tk.Frame(tk_row_frame)
                element.element_frame = element_frame

                justification = tk.LEFT
                if element.justification is not None:
                    if element.justification.startswith('l'):
                        justification = tk.LEFT
                    elif element.justification.startswith('r'):
                        justification = tk.RIGHT
                    elif element.justification.startswith('c'):
                        justification = tk.CENTER

                element.TKStringVar = tk.StringVar()
                element.TKListbox = element.Widget = tk.Listbox(
                    element_frame,
                    height=element_size[1],
                    width=width,
                    selectmode=element.SelectMode,
                    font=font,
                    exportselection=False,
                )
                # On OLD versions of tkinter the justify option isn't available
                try:
                    element.Widget.config(justify=justification)
                except:
                    pass

                element.Widget.config(highlightthickness=0)
                for index, item in enumerate(element.Values):
                    element.TKListbox.insert(tk.END, item)
                    if element.DefaultValues is not None and item in element.DefaultValues:
                        element.TKListbox.selection_set(index)
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKListbox.configure(background=element.BackgroundColor)
                if element.HighlightBackgroundColor is not None and element.HighlightBackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKListbox.config(selectbackground=element.HighlightBackgroundColor)
                if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
                    element.TKListbox.configure(fg=text_color)
                if element.HighlightTextColor is not None and element.HighlightTextColor != COLOR_SYSTEM_DEFAULT:
                    element.TKListbox.config(selectforeground=element.HighlightTextColor)
                if element.ChangeSubmits:
                    element.TKListbox.bind('<<ListboxSelect>>', element._ListboxSelectHandler)

                if not element.NoScrollbar:
                    _make_ttk_scrollbar(element, 'v', toplevel_form)
                    element.Widget.configure(yscrollcommand=element.vsb.set)
                    element.vsb.pack(side=tk.RIGHT, fill='y')

                # Horizontal scrollbar
                if element.HorizontalScroll:
                    _make_ttk_scrollbar(element, 'h', toplevel_form)
                    element.hsb.pack(side=tk.BOTTOM, fill='x')
                    element.Widget.configure(xscrollcommand=element.hsb.set)

                if not element.NoScrollbar or element.HorizontalScroll:
                    # Chr0nic
                    element.Widget.bind('<Enter>', lambda event, em=element: testMouseHook(em))
                    element.Widget.bind('<Leave>', lambda event, em=element: testMouseUnhook(em))

                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=fill, expand=expand)
                element.TKListbox.pack(side=tk.LEFT, fill=fill, expand=expand)
                if element.visible is False:
                    element._pack_forget_save_settings(alternate_widget=element_frame)
                    # element_frame.pack_forget()
                if element.BindReturnKey:
                    element.TKListbox.bind('<Return>', element._ListboxSelectHandler)
                    element.TKListbox.bind('<Double-Button-1>', element._ListboxSelectHandler)
                if element.Disabled is True:
                    element.TKListbox['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKListbox, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)
            # -------------------------  MULTILINE placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_MULTILINE:
                element = element  # type: Multiline
                width, height = element_size
                bd = element.BorderWidth
                element.element_frame = element_frame = tk.Frame(tk_row_frame)

                # if element.no_scrollbar:
                element.TKText = element.Widget = tk.Text(element_frame, width=width, height=height, bd=bd, font=font, relief=RELIEF_SUNKEN)
                # else:
                #     element.TKText = element.Widget = tk.scrolledtext.ScrolledText(element_frame, width=width, height=height, bd=bd, font=font, relief=RELIEF_SUNKEN)

                if not element.no_scrollbar:
                    _make_ttk_scrollbar(element, 'v', toplevel_form)

                    element.Widget.configure(yscrollcommand=element.vsb.set)
                    element.vsb.pack(side=tk.RIGHT, fill='y')

                # Horizontal scrollbar
                if element.HorizontalScroll:
                    element.TKText.config(wrap='none')
                    _make_ttk_scrollbar(element, 'h', toplevel_form)
                    element.hsb.pack(side=tk.BOTTOM, fill='x')
                    element.Widget.configure(xscrollcommand=element.hsb.set)
                else:
                    element.TKText.config(wrap='word')

                if element.wrap_lines is True:
                    element.TKText.config(wrap='word')
                elif element.wrap_lines is False:
                    element.TKText.config(wrap='none')

                if not element.no_scrollbar or element.HorizontalScroll:
                    # Chr0nic
                    element.TKText.bind('<Enter>', lambda event, em=element: testMouseHook(em))
                    element.TKText.bind('<Leave>', lambda event, em=element: testMouseUnhook(em))

                if element.DefaultText:
                    element.TKText.insert(1.0, element.DefaultText)  # set the default text
                element.TKText.config(highlightthickness=0)
                if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
                    element.TKText.configure(fg=text_color, selectbackground=text_color)
                    element.TKText.config(insertbackground=text_color)
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKText.configure(background=element.BackgroundColor, selectforeground=element.BackgroundColor)
                if element.selected_background_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKText.configure(selectbackground=element.selected_background_color)
                if element.selected_text_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKText.configure(selectforeground=element.selected_text_color)
                element.TKText.tag_configure('center', justify='center')
                element.TKText.tag_configure('left', justify='left')
                element.TKText.tag_configure('right', justify='right')

                if element.Justification.startswith('l'):
                    element.TKText.tag_add('left', 1.0, 'end')
                    element.justification_tag = 'left'
                elif element.Justification.startswith('r'):
                    element.TKText.tag_add('right', 1.0, 'end')
                    element.justification_tag = 'right'
                elif element.Justification.startswith('c'):
                    element.TKText.tag_add('center', 1.0, 'end')
                    element.justification_tag = 'center'
                # if DEFAULT_SCROLLBAR_COLOR not in (None, COLOR_SYSTEM_DEFAULT):               # only works on Linux so not including it
                #     element.TKText.vbar.config(troughcolor=DEFAULT_SCROLLBAR_COLOR)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)

                element.element_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=fill, expand=expand)
                element.Widget.pack(side=tk.LEFT, fill=fill, expand=expand)

                if element.visible is False:
                    element._pack_forget_save_settings(alternate_widget=element_frame)
                    # element.element_frame.pack_forget()
                else:
                    # Chr0nic
                    element.TKText.bind('<Enter>', lambda event, em=element: testMouseHook(em))
                    element.TKText.bind('<Leave>', lambda event, em=element: testMouseUnhook(em))
                if element.ChangeSubmits:
                    element.TKText.bind('<Key>', element._KeyboardHandler)
                if element.EnterSubmits:
                    element.TKText.bind('<Return>', element._ReturnKeyHandler)
                if element.Focus is True or (toplevel_form.UseDefaultFocus and not toplevel_form.FocusSet):
                    toplevel_form.FocusSet = True
                    element.TKText.focus_set()

                if element.Disabled is True:
                    element.TKText['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)

                if element.reroute_cprint:
                    cprint_set_output_destination(toplevel_form, element.Key)

                _add_right_click_menu_and_grab(element)

                if element.reroute_stdout:
                    element.reroute_stdout_to_here()
                if element.reroute_stderr:
                    element.reroute_stderr_to_here()

                # row_should_expand = True
            # -------------------------  CHECKBOX pleacement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_CHECKBOX:
                element = element  # type: Checkbox
                width = 0 if auto_size_text else element_size[0]
                default_value = element.InitialState
                element.TKIntVar = tk.IntVar()
                element.TKIntVar.set(default_value if default_value is not None else 0)

                element.TKCheckbutton = element.Widget = tk.Checkbutton(
                    tk_row_frame,
                    anchor=tk.NW,
                    text=element.Text,
                    width=width,
                    variable=element.TKIntVar,
                    bd=border_depth,
                    font=font,
                )
                if element.ChangeSubmits:
                    element.TKCheckbutton.configure(command=element._CheckboxHandler)
                if element.Disabled:
                    element.TKCheckbutton.configure(state='disable')
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKCheckbutton.configure(background=element.BackgroundColor)
                    element.TKCheckbutton.configure(selectcolor=element.CheckboxBackgroundColor)  # The background of the checkbox
                    element.TKCheckbutton.configure(activebackground=element.BackgroundColor)
                if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
                    element.TKCheckbutton.configure(fg=text_color)
                    element.TKCheckbutton.configure(activeforeground=element.TextColor)

                element.Widget.configure(highlightthickness=element.highlight_thickness)
                if element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKCheckbutton.config(highlightbackground=element.BackgroundColor)
                if element.TextColor != COLOR_SYSTEM_DEFAULT:
                    element.TKCheckbutton.config(highlightcolor=element.TextColor)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKCheckbutton.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKCheckbutton.pack_forget()
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKCheckbutton, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

            # -------------------------  PROGRESS placement element  ------------------------- #
            elif element_type == ELEM_TYPE_PROGRESS_BAR:
                element = element  # type: ProgressBar
                if element.size_px != (None, None):
                    progress_length, progress_width = element.size_px
                else:
                    width = element_size[0]
                    fnt = tkinter.font.Font()
                    char_width = fnt.measure('A')  # single character width
                    progress_length = width * char_width
                    progress_width = element_size[1]
                direction = element.Orientation
                if element.BarColor != (None, None):  # if element has a bar color, use it
                    bar_color = element.BarColor
                else:
                    bar_color = DEFAULT_PROGRESS_BAR_COLOR
                if element.Orientation.lower().startswith('h'):
                    base_style_name = '.Horizontal.TProgressbar'
                else:
                    base_style_name = '.Vertical.TProgressbar'
                style_name = _make_ttk_style_name(base_style_name, element, primary_style=True)
                element.TKProgressBar = TKProgressBar(
                    tk_row_frame,
                    element.MaxValue,
                    progress_length,
                    progress_width,
                    orientation=direction,
                    BarColor=bar_color,
                    border_width=element.BorderWidth,
                    relief=element.Relief,
                    ttk_theme=toplevel_form.TtkTheme,
                    key=element.Key,
                    style_name=style_name,
                )
                element.Widget = element.TKProgressBar.TKProgressBarForReal
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKProgressBar.TKProgressBarForReal.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings(alternate_widget=element.TKProgressBar.TKProgressBarForReal)
                    # element.TKProgressBar.TKProgressBarForReal.pack_forget()
                _add_right_click_menu_and_grab(element)

                # -------------------------  RADIO placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_RADIO:
                element = element  # type: Radio
                width = 0 if auto_size_text else element_size[0]
                default_value = element.InitialState
                ID = element.GroupID
                # see if ID has already been placed
                value = EncodeRadioRowCol(form.ContainerElemementNumber, row_num, col_num)  # value to set intvar to if this radio is selected
                element.EncodedRadioValue = value
                if ID in toplevel_form.RadioDict:
                    RadVar = toplevel_form.RadioDict[ID]
                else:
                    RadVar = tk.IntVar()
                    toplevel_form.RadioDict[ID] = RadVar
                element.TKIntVar = RadVar  # store the RadVar in Radio object
                if default_value:  # if this radio is the one selected, set RadVar to match
                    element.TKIntVar.set(value)
                element.TKRadio = element.Widget = tk.Radiobutton(
                    tk_row_frame,
                    anchor=tk.NW,
                    text=element.Text,
                    width=width,
                    variable=element.TKIntVar,
                    value=value,
                    bd=border_depth,
                    font=font,
                )
                if element.ChangeSubmits:
                    element.TKRadio.configure(command=element._RadioHandler)
                if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKRadio.configure(background=element.BackgroundColor)
                    element.TKRadio.configure(selectcolor=element.CircleBackgroundColor)
                    element.TKRadio.configure(activebackground=element.BackgroundColor)
                if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
                    element.TKRadio.configure(fg=text_color)
                    element.TKRadio.configure(activeforeground=text_color)

                element.Widget.configure(highlightthickness=1)
                if element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKRadio.config(highlightbackground=element.BackgroundColor)
                if element.TextColor != COLOR_SYSTEM_DEFAULT:
                    element.TKRadio.config(highlightcolor=element.TextColor)

                if element.Disabled:
                    element.TKRadio['state'] = 'disabled'
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKRadio.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKRadio.pack_forget()
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKRadio, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

                # -------------------------  SPIN placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_SPIN:
                element = element  # type: Spin
                width, height = element_size
                width = 0 if auto_size_text else element_size[0]
                element.TKStringVar = tk.StringVar()
                element.TKSpinBox = element.Widget = tk.Spinbox(tk_row_frame, values=element.Values, textvariable=element.TKStringVar, width=width, bd=border_depth)
                if element.DefaultValue is not None:
                    element.TKStringVar.set(element.DefaultValue)
                element.TKSpinBox.configure(font=font)  # set wrap to width of widget
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element.TKSpinBox.configure(background=element.BackgroundColor)
                    element.TKSpinBox.configure(buttonbackground=element.BackgroundColor)
                if text_color not in (None, COLOR_SYSTEM_DEFAULT):
                    element.TKSpinBox.configure(fg=text_color)
                    element.TKSpinBox.config(insertbackground=text_color)
                element.Widget.config(highlightthickness=0)
                if element.wrap is True:
                    element.Widget.configure(wrap=True)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKSpinBox.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.TKSpinBox.pack_forget()
                if element.ChangeSubmits:
                    element.TKSpinBox.configure(command=element._SpinboxSelectHandler)
                    # element.TKSpinBox.bind('<ButtonRelease-1>', element._SpinChangedHandler)
                    # element.TKSpinBox.bind('<Up>', element._SpinChangedHandler)
                    # element.TKSpinBox.bind('<Down>', element._SpinChangedHandler)
                if element.Readonly:
                    element.TKSpinBox['state'] = 'readonly'
                if element.Disabled is True:  # note overrides readonly if disabled
                    element.TKSpinBox['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKSpinBox, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                if element.BindReturnKey:
                    element.TKSpinBox.bind('<Return>', element._SpinboxSelectHandler)
                _add_right_click_menu_and_grab(element)
                # -------------------------  IMAGE placement element  ------------------------- #
            elif element_type == ELEM_TYPE_IMAGE:
                element = element  # type: Image
                try:
                    if element.Filename is not None:
                        photo = tk.PhotoImage(file=element.Filename)
                    elif element.Data is not None:
                        photo = tk.PhotoImage(data=element.Data)
                    else:
                        photo = None

                    if photo is not None:
                        if element.ImageSubsample:
                            photo = photo.subsample(element.ImageSubsample)
                        if element.zoom:
                            photo = photo.zoom(element.zoom)
                        # print('*ERROR laying out form.... Image Element has no image specified*')
                except Exception as e:
                    photo = None
                    _error_popup_with_traceback(
                        'Your Window has an Image Element with a problem',
                        'The traceback will show you the Window with the problem layout',
                        f'Look in this Window\'s layout for an Image element that has a key of {element.Key}',
                        'The error occuring is:',
                        e,
                    )

                element.tktext_label = element.Widget = tk.Label(tk_row_frame, bd=0)

                if photo is not None:
                    if element_size == (None, None) or element_size is None or element_size == toplevel_form.DefaultElementSize:
                        width, height = photo.width(), photo.height()
                    else:
                        width, height = element_size
                    element.tktext_label.config(image=photo, width=width, height=height)

                if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                    element.tktext_label.config(background=element.BackgroundColor)

                element.tktext_label.image = photo
                # tktext_label.configure(anchor=tk.NW, image=photo)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.tktext_label.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)

                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element.tktext_label.pack_forget()
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.tktext_label, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                if element.EnableEvents and element.tktext_label is not None:
                    element.tktext_label.bind('<ButtonPress-1>', element._ClickHandler)

                _add_right_click_menu_and_grab(element)

                # -------------------------  Canvas placement element  ------------------------- #
            elif element_type == ELEM_TYPE_CANVAS:
                element = element  # type: Canvas
                width, height = element_size
                if element._TKCanvas is None:
                    element._TKCanvas = tk.Canvas(tk_row_frame, width=width, height=height, bd=border_depth)
                else:
                    element._TKCanvas.master = tk_row_frame
                element.Widget = element._TKCanvas

                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element._TKCanvas.configure(background=element.BackgroundColor, highlightthickness=0)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element._TKCanvas.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element._TKCanvas.pack_forget()
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element._TKCanvas, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

                # -------------------------  Graph placement element  ------------------------- #
            elif element_type == ELEM_TYPE_GRAPH:
                element = element  # type: Graph
                width, height = element_size
                # I don't know why TWO canvases were being defined, on inside the other.  Was it so entire canvas can move?
                # if element._TKCanvas is None:
                #     element._TKCanvas = tk.Canvas(tk_row_frame, width=width, height=height, bd=border_depth)
                # else:
                #     element._TKCanvas.master = tk_row_frame
                element._TKCanvas2 = element.Widget = tk.Canvas(tk_row_frame, width=width, height=height, bd=border_depth)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element._TKCanvas2.pack(side=tk.LEFT, expand=expand, fill=fill)
                element._TKCanvas2.addtag_all('mytag')
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    element._TKCanvas2.configure(background=element.BackgroundColor, highlightthickness=0)
                    # element._TKCanvas.configure(background=element.BackgroundColor, highlightthickness=0)
                element._TKCanvas2.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # element._TKCanvas2.pack_forget()
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element._TKCanvas2, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                if element.ChangeSubmits:
                    element._TKCanvas2.bind('<ButtonRelease-1>', element.ButtonReleaseCallBack)
                    element._TKCanvas2.bind('<ButtonPress-1>', element.ButtonPressCallBack)
                if element.DragSubmits:
                    element._TKCanvas2.bind('<Motion>', element.MotionCallBack)
                _add_right_click_menu_and_grab(element)
            # -------------------------  MENU placement element  ------------------------- #
            elif element_type == ELEM_TYPE_MENUBAR:
                element = element  # type: MenuBar
                menu_def = element.MenuDefinition
                element.TKMenu = element.Widget = tk.Menu(toplevel_form.TKroot, tearoff=element.Tearoff, tearoffcommand=element._tearoff_menu_callback)  # create the menubar
                menubar = element.TKMenu
                if font is not None:  # if a font is used, make sure it's saved in the element
                    element.Font = font
                for menu_entry in menu_def:
                    baritem = tk.Menu(menubar, tearoff=element.Tearoff, tearoffcommand=element._tearoff_menu_callback)
                    if element.BackgroundColor not in (COLOR_SYSTEM_DEFAULT, None):
                        baritem.config(bg=element.BackgroundColor)
                        baritem.config(activeforeground=element.BackgroundColor)
                    if element.TextColor not in (COLOR_SYSTEM_DEFAULT, None):
                        baritem.config(fg=element.TextColor)
                        baritem.config(activebackground=element.TextColor)
                    if element.DisabledTextColor not in (COLOR_SYSTEM_DEFAULT, None):
                        baritem.config(disabledforeground=element.DisabledTextColor)
                    if font is not None:
                        baritem.config(font=font)
                    pos = menu_entry[0].find(MENU_SHORTCUT_CHARACTER)
                    # print(pos)
                    if pos != -1:
                        if pos == 0 or menu_entry[0][pos - len(MENU_SHORTCUT_CHARACTER)] != '\\':
                            menu_entry[0] = menu_entry[0][:pos] + menu_entry[0][pos + 1 :]
                    if menu_entry[0][0] == MENU_DISABLED_CHARACTER:
                        menubar.add_cascade(label=menu_entry[0][len(MENU_DISABLED_CHARACTER) :], menu=baritem, underline=pos - 1)
                        menubar.entryconfig(menu_entry[0][len(MENU_DISABLED_CHARACTER) :], state='disabled')
                    else:
                        menubar.add_cascade(label=menu_entry[0], menu=baritem, underline=pos)

                    if len(menu_entry) > 1:
                        AddMenuItem(baritem, menu_entry[1], element)
                toplevel_form.TKroot.configure(menu=element.TKMenu)
            # -------------------------  Frame placement element  ------------------------- #
            elif element_type == ELEM_TYPE_FRAME:
                element = element  # type: Frame
                labeled_frame = element.Widget = tk.LabelFrame(tk_row_frame, text=element.Title, relief=element.Relief)
                element.TKFrame = labeled_frame
                PackFormIntoFrame(element, labeled_frame, toplevel_form)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                if element.VerticalAlignment is not None:
                    anchor = tk.CENTER  # Default to center if a bad choice is made
                    if element.VerticalAlignment.lower().startswith('t'):
                        anchor = tk.N
                    if element.VerticalAlignment.lower().startswith('c'):
                        anchor = tk.CENTER
                    if element.VerticalAlignment.lower().startswith('b'):
                        anchor = tk.S
                    labeled_frame.pack(side=tk.LEFT, anchor=anchor, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                else:
                    labeled_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)

                if element.Size != (None, None):
                    labeled_frame.config(width=element.Size[0], height=element.Size[1])
                    labeled_frame.pack_propagate(0)
                if not element.visible:
                    element._pack_forget_save_settings()
                    # labeled_frame.pack_forget()
                if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None:
                    labeled_frame.configure(
                        background=element.BackgroundColor,
                        highlightbackground=element.BackgroundColor,
                        highlightcolor=element.BackgroundColor,
                    )
                if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
                    labeled_frame.configure(foreground=element.TextColor)
                if font is not None:
                    labeled_frame.configure(font=font)
                if element.TitleLocation is not None:
                    labeled_frame.configure(labelanchor=element.TitleLocation)
                if element.BorderWidth is not None:
                    labeled_frame.configure(borderwidth=element.BorderWidth)
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(labeled_frame, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)
                # row_should_expand=True
            # -------------------------  Tab placement element  ------------------------- #
            elif element_type == ELEM_TYPE_TAB:
                element = element  # type: Tab
                form = form  # type: TabGroup
                element.TKFrame = element.Widget = tk.Frame(form.TKNotebook)
                PackFormIntoFrame(element, element.TKFrame, toplevel_form)
                state = 'normal'
                if element.Disabled:
                    state = 'disabled'
                if element.visible is False:
                    state = 'hidden'
                # this code will add an image to the tab. Use it when adding the image on a tab enhancement
                try:
                    if element.Filename is not None:
                        photo = tk.PhotoImage(file=element.Filename)
                    elif element.Data is not None:
                        photo = tk.PhotoImage(data=element.Data)
                    else:
                        photo = None

                    if element.ImageSubsample and photo is not None:
                        photo = photo.subsample(element.ImageSubsample)
                    if element.zoom and photo is not None:
                        photo = photo.zoom(element.zoom)
                        # print('*ERROR laying out form.... Image Element has no image specified*')
                except Exception as e:
                    photo = None
                    _error_popup_with_traceback(
                        'Your Window has an Tab Element with an IMAGE problem',
                        'The traceback will show you the Window with the problem layout',
                        f'Look in this Window\'s layout for an Image element that has a key of {element.Key}',
                        'The error occuring is:',
                        e,
                    )

                element.photo = photo
                if photo is not None:
                    if element_size == (None, None) or element_size is None or element_size == toplevel_form.DefaultElementSize:
                        width, height = photo.width(), photo.height()
                    else:
                        width, height = element_size
                    element.tktext_label = tk.Label(tk_row_frame, image=photo, width=width, height=height, bd=0)
                else:
                    element.tktext_label = tk.Label(tk_row_frame, bd=0)
                if photo is not None:
                    form.TKNotebook.add(element.TKFrame, text=element.Title, compound=tk.LEFT, state=state, image=photo)

                # element.photo_image = tk.PhotoImage(data=DEFAULT_BASE64_ICON)
                # form.TKNotebook.add(element.TKFrame, text=element.Title, compound=tk.LEFT, state=state,image = element.photo_image)

                form.TKNotebook.add(element.TKFrame, text=element.Title, state=state)
                # July 28 2022 removing the expansion and pack as a test
                # expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                # form.TKNotebook.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=fill, expand=expand)

                element.ParentNotebook = form.TKNotebook
                element.TabID = form.TabCount
                form.tab_index_to_key[element.TabID] = element.key  # has a list of the tabs in the notebook and their associated key
                form.TabCount += 1
                if element.BackgroundColor not in (COLOR_SYSTEM_DEFAULT, None):
                    element.TKFrame.configure(
                        background=element.BackgroundColor,
                        highlightbackground=element.BackgroundColor,
                        highlightcolor=element.BackgroundColor,
                    )

                # if element.BorderWidth is not None:
                #     element.TKFrame.configure(borderwidth=element.BorderWidth)
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKFrame, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)
                # row_should_expand = True
            # -------------------------  TabGroup placement element  ------------------------- #
            elif element_type == ELEM_TYPE_TAB_GROUP:
                element = element  # type: TabGroup
                # custom_style = str(element.Key) + 'customtab.TNotebook'
                custom_style = _make_ttk_style_name('.TNotebook', element, primary_style=True)
                style = ttk.Style()
                _change_ttk_theme(style, toplevel_form.TtkTheme)

                if element.TabLocation is not None:
                    position_dict = {
                        'left': 'w',
                        'right': 'e',
                        'top': 'n',
                        'bottom': 's',
                        'lefttop': 'wn',
                        'leftbottom': 'ws',
                        'righttop': 'en',
                        'rightbottom': 'es',
                        'bottomleft': 'sw',
                        'bottomright': 'se',
                        'topleft': 'nw',
                        'topright': 'ne',
                    }
                    try:
                        tab_position = position_dict[element.TabLocation]
                    except:
                        tab_position = position_dict['top']
                    style.configure(custom_style, tabposition=tab_position)

                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    style.configure(custom_style, background=element.BackgroundColor)

                # FINALLY the proper styling to get tab colors!
                if element.SelectedTitleColor is not None and element.SelectedTitleColor != COLOR_SYSTEM_DEFAULT:
                    style.map(custom_style + '.Tab', foreground=[('selected', element.SelectedTitleColor)])
                if element.SelectedBackgroundColor is not None and element.SelectedBackgroundColor != COLOR_SYSTEM_DEFAULT:
                    style.map(custom_style + '.Tab', background=[('selected', element.SelectedBackgroundColor)])
                if element.TabBackgroundColor is not None and element.TabBackgroundColor != COLOR_SYSTEM_DEFAULT:
                    style.configure(custom_style + '.Tab', background=element.TabBackgroundColor)
                if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
                    style.configure(custom_style + '.Tab', foreground=element.TextColor)
                if element.BorderWidth is not None:
                    style.configure(custom_style, borderwidth=element.BorderWidth)
                if element.TabBorderWidth is not None:
                    style.configure(custom_style + '.Tab', borderwidth=element.TabBorderWidth)  # if ever want to get rid of border around the TABS themselves
                if element.FocusColor not in (None, COLOR_SYSTEM_DEFAULT):
                    style.configure(custom_style + '.Tab', focuscolor=element.FocusColor)

                style.configure(custom_style + '.Tab', font=font)
                element.Style = style
                element.StyleName = custom_style
                element.TKNotebook = element.Widget = ttk.Notebook(tk_row_frame, style=custom_style)

                PackFormIntoFrame(element, toplevel_form.TKroot, toplevel_form)

                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKNotebook.pack(anchor=tk.SW, side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=fill, expand=expand)

                if element.ChangeSubmits:
                    element.TKNotebook.bind('<<NotebookTabChanged>>', element._TabGroupSelectHandler)
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKNotebook, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                if element.Size != (None, None):
                    element.TKNotebook.configure(width=element.Size[0], height=element.Size[1])
                _add_right_click_menu_and_grab(element)
                if element.visible is False:
                    element._pack_forget_save_settings()
                # row_should_expand = True
                # -------------------  SLIDER placement element  ------------------------- #
            elif element_type == ELEM_TYPE_INPUT_SLIDER:
                element = element  # type: Slider
                slider_length = element_size[0] * _char_width_in_pixels(font)
                slider_width = element_size[1]
                element.TKIntVar = tk.IntVar()
                element.TKIntVar.set(element.DefaultValue)
                if element.Orientation.startswith('v'):
                    range_from = element.Range[1]
                    range_to = element.Range[0]
                    slider_length += DEFAULT_MARGINS[1] * (element_size[0] * 2)  # add in the padding
                else:
                    range_from = element.Range[0]
                    range_to = element.Range[1]
                tkscale = element.Widget = tk.Scale(
                    tk_row_frame,
                    orient=element.Orientation,
                    variable=element.TKIntVar,
                    from_=range_from,
                    to_=range_to,
                    resolution=element.Resolution,
                    length=slider_length,
                    width=slider_width,
                    bd=element.BorderWidth,
                    relief=element.Relief,
                    font=font,
                    tickinterval=element.TickInterval,
                )
                tkscale.config(highlightthickness=0)
                if element.ChangeSubmits:
                    tkscale.config(command=element._SliderChangedHandler)
                if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
                    tkscale.configure(background=element.BackgroundColor)
                if element.TroughColor != COLOR_SYSTEM_DEFAULT:
                    tkscale.config(troughcolor=element.TroughColor)
                if element.DisableNumericDisplay:
                    tkscale.config(showvalue=0)
                if text_color not in (None, COLOR_SYSTEM_DEFAULT):
                    tkscale.configure(fg=text_color)
                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                tkscale.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # tkscale.pack_forget()
                element.TKScale = tkscale
                if element.Disabled is True:
                    element.TKScale['state'] = 'disabled'
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKScale, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

            # -------------------------  TABLE placement element  ------------------------- #
            elif element_type == ELEM_TYPE_TABLE:
                element = element  # type: Table
                element.element_frame = frame = tk.Frame(tk_row_frame)
                element.table_frame = frame
                height = element.NumRows
                if element.Justification.startswith('l'):
                    anchor = tk.W
                elif element.Justification.startswith('r'):
                    anchor = tk.E
                else:
                    anchor = tk.CENTER
                column_widths = {}
                # create column width list
                for row in element.Values:
                    for i, col in enumerate(row):
                        col_width = min(len(str(col)), element.MaxColumnWidth)
                        try:
                            if col_width > column_widths[i]:
                                column_widths[i] = col_width
                        except:
                            column_widths[i] = col_width

                if element.ColumnsToDisplay is None:
                    displaycolumns = element.ColumnHeadings if element.ColumnHeadings is not None else element.Values[0]
                else:
                    displaycolumns = []
                    for i, should_display in enumerate(element.ColumnsToDisplay):
                        if should_display:
                            if element.ColumnHeadings is not None:
                                displaycolumns.append(element.ColumnHeadings[i])
                            else:
                                displaycolumns.append(str(i))

                column_headings = element.ColumnHeadings if element.ColumnHeadings is not None else displaycolumns
                if element.DisplayRowNumbers:  # if display row number, tack on the numbers to front of columns
                    displaycolumns = [
                        element.RowHeaderText,
                    ] + displaycolumns
                    if column_headings is not None:
                        column_headings = [
                            element.RowHeaderText,
                        ] + element.ColumnHeadings
                    else:
                        column_headings = [
                            element.RowHeaderText,
                        ] + displaycolumns
                element.TKTreeview = element.Widget = ttk.Treeview(
                    frame,
                    columns=column_headings,
                    displaycolumns=displaycolumns,
                    show='headings',
                    height=height,
                    selectmode=element.SelectMode,
                )
                treeview = element.TKTreeview
                if element.DisplayRowNumbers:
                    treeview.heading(element.RowHeaderText, text=element.RowHeaderText)  # make a dummy heading
                    row_number_header_width = _string_width_in_pixels(element.HeaderFont, element.RowHeaderText) + 10
                    row_number_width = _string_width_in_pixels(font, str(len(element.Values))) + 10
                    row_number_width = max(row_number_header_width, row_number_width)
                    treeview.column(element.RowHeaderText, width=row_number_width, minwidth=10, anchor=anchor, stretch=0)

                headings = element.ColumnHeadings if element.ColumnHeadings is not None else element.Values[0]
                for i, heading in enumerate(headings):
                    # heading = str(heading)
                    treeview.heading(heading, text=heading)
                    if element.AutoSizeColumns:
                        col_width = column_widths.get(i, len(heading))  # in case more headings than there are columns of data
                        width = max(
                            col_width * _char_width_in_pixels(font),
                            len(heading) * _char_width_in_pixels(element.HeaderFont),
                        )
                    else:
                        try:
                            width = element.ColumnWidths[i] * _char_width_in_pixels(font)
                        except:
                            width = element.DefaultColumnWidth * _char_width_in_pixels(font)
                    if element.cols_justification is not None:
                        try:
                            if element.cols_justification[i].startswith('l'):
                                col_anchor = tk.W
                            elif element.cols_justification[i].startswith('r'):
                                col_anchor = tk.E
                            elif element.cols_justification[i].startswith('c'):
                                col_anchor = tk.CENTER
                            else:
                                col_anchor = anchor

                        except:  # likely didn't specify enough entries (must be one per col)
                            col_anchor = anchor
                    else:
                        col_anchor = anchor
                    treeview.column(heading, width=width, minwidth=10, anchor=col_anchor, stretch=element.expand_x)
                # Insert values into the tree
                for i, value in enumerate(element.Values):
                    if element.DisplayRowNumbers:
                        value = [i + element.StartingRowNumber] + value
                    id = treeview.insert('', 'end', text=value, iid=i + 1, values=value, tag=i)
                    element.tree_ids.append(id)
                if element.AlternatingRowColor not in (None, COLOR_SYSTEM_DEFAULT):  # alternating colors
                    for row in range(0, len(element.Values), 2):
                        treeview.tag_configure(row, background=element.AlternatingRowColor)
                if element.RowColors is not None:  # individual row colors
                    for row_def in element.RowColors:
                        if len(row_def) == 2:  # only background is specified
                            treeview.tag_configure(row_def[0], background=row_def[1])
                        else:
                            treeview.tag_configure(row_def[0], background=row_def[2], foreground=row_def[1])
                # ------ Do Styling of Colors -----
                # style_name = str(element.Key) + 'customtable.Treeview'
                style_name = _make_ttk_style_name('.Treeview', element, primary_style=True)
                element.table_ttk_style_name = style_name
                table_style = ttk.Style()
                element.ttk_style = table_style

                _change_ttk_theme(table_style, toplevel_form.TtkTheme)

                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    table_style.configure(
                        style_name,
                        background=element.BackgroundColor,
                        fieldbackground=element.BackgroundColor,
                    )
                    if element.SelectedRowColors[1] is not None:
                        table_style.map(
                            style_name,
                            background=_fixed_map(table_style, style_name, 'background', element.SelectedRowColors),
                        )
                if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
                    table_style.configure(style_name, foreground=element.TextColor)
                    if element.SelectedRowColors[0] is not None:
                        table_style.map(
                            style_name,
                            foreground=_fixed_map(table_style, style_name, 'foreground', element.SelectedRowColors),
                        )
                if element.RowHeight is not None:
                    table_style.configure(style_name, rowheight=element.RowHeight)
                else:
                    table_style.configure(style_name, rowheight=_char_height_in_pixels(font))
                if element.HeaderTextColor is not None and element.HeaderTextColor != COLOR_SYSTEM_DEFAULT:
                    table_style.configure(style_name + '.Heading', foreground=element.HeaderTextColor)
                if element.HeaderBackgroundColor is not None and element.HeaderBackgroundColor != COLOR_SYSTEM_DEFAULT:
                    table_style.configure(style_name + '.Heading', background=element.HeaderBackgroundColor)
                if element.HeaderFont is not None:
                    table_style.configure(style_name + '.Heading', font=element.HeaderFont)
                else:
                    table_style.configure(style_name + '.Heading', font=font)
                if element.HeaderBorderWidth is not None:
                    table_style.configure(style_name + '.Heading', borderwidth=element.HeaderBorderWidth)
                if element.HeaderRelief is not None:
                    table_style.configure(style_name + '.Heading', relief=element.HeaderRelief)
                table_style.configure(style_name, font=font)
                if element.BorderWidth is not None:
                    table_style.configure(style_name, borderwidth=element.BorderWidth)

                if element.HeaderBackgroundColor not in (
                    None,
                    COLOR_SYSTEM_DEFAULT,
                ) and element.HeaderTextColor not in (None, COLOR_SYSTEM_DEFAULT):
                    table_style.map(
                        style_name + '.Heading',
                        background=[
                            ('pressed', '!focus', element.HeaderBackgroundColor),
                            ('active', element.HeaderTextColor),
                        ],
                    )
                    table_style.map(
                        style_name + '.Heading',
                        foreground=[
                            ('pressed', '!focus', element.HeaderTextColor),
                            ('active', element.HeaderBackgroundColor),
                        ],
                    )

                treeview.configure(style=style_name)
                # scrollable_frame.pack(side=tk.LEFT,  padx=elementpad[0], pady=elementpad[1], expand=True, fill='both')
                if element.enable_click_events is True:
                    treeview.bind('<ButtonRelease-1>', element._table_clicked)
                if element.right_click_selects:
                    if running_mac():
                        treeview.bind('<Button-2>', element._table_clicked)
                    else:
                        treeview.bind('<Button-3>', element._table_clicked)
                treeview.bind('<<TreeviewSelect>>', element._treeview_selected)
                if element.BindReturnKey:
                    treeview.bind('<Return>', element._treeview_double_click)
                    treeview.bind('<Double-Button-1>', element._treeview_double_click)

                if not element.HideVerticalScroll:
                    _make_ttk_scrollbar(element, 'v', toplevel_form)

                    element.Widget.configure(yscrollcommand=element.vsb.set)
                    element.vsb.pack(side=tk.RIGHT, fill='y')

                # Horizontal scrollbar
                if not element.VerticalScrollOnly:
                    # element.Widget.config(wrap='none')
                    _make_ttk_scrollbar(element, 'h', toplevel_form)
                    element.hsb.pack(side=tk.BOTTOM, fill='x')
                    element.Widget.configure(xscrollcommand=element.hsb.set)

                if not element.HideVerticalScroll or not element.VerticalScrollOnly:
                    # Chr0nic
                    element.Widget.bind('<Enter>', lambda event, em=element: testMouseHook(em))
                    element.Widget.bind('<Leave>', lambda event, em=element: testMouseUnhook(em))

                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKTreeview.pack(side=tk.LEFT, padx=0, pady=0, expand=expand, fill=fill)
                frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings(alternate_widget=element.element_frame)  # seems like it should be the frame if following other elements conventions
                    # element.TKTreeview.pack_forget()
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

                if tclversion_detailed == '8.6.9' and ENABLE_TREEVIEW_869_PATCH:
                    # print('*** tk version 8.6.9 detected.... patching ttk treeview code ***')
                    table_style.map(
                        style_name,
                        foreground=_fixed_map(table_style, style_name, 'foreground', element.SelectedRowColors),
                        background=_fixed_map(table_style, style_name, 'background', element.SelectedRowColors),
                    )
            # -------------------------  Tree placement element  ------------------------- #
            elif element_type == ELEM_TYPE_TREE:
                element = element  # type: Tree
                element.element_frame = element_frame = tk.Frame(tk_row_frame)

                height = element.NumRows
                if element.Justification.startswith('l'):  # justification
                    anchor = tk.W
                elif element.Justification.startswith('r'):
                    anchor = tk.E
                else:
                    anchor = tk.CENTER

                if element.ColumnsToDisplay is None:  # Which cols to display
                    displaycolumns = element.ColumnHeadings
                else:
                    displaycolumns = []
                    for i, should_display in enumerate(element.ColumnsToDisplay):
                        if should_display:
                            displaycolumns.append(element.ColumnHeadings[i])
                column_headings = element.ColumnHeadings
                # ------------- GET THE TREEVIEW WIDGET -------------
                element.TKTreeview = element.Widget = ttk.Treeview(
                    element_frame,
                    columns=column_headings,
                    displaycolumns=displaycolumns,
                    show='tree headings' if column_headings is not None else 'tree',
                    height=height,
                    selectmode=element.SelectMode,
                )
                treeview = element.TKTreeview
                max_widths = {}
                for key, node in element.TreeData.tree_dict.items():
                    for i, value in enumerate(node.values):
                        max_width = max_widths.get(i, 0)
                        if len(str(value)) > max_width:
                            max_widths[i] = len(str(value))

                if element.ColumnHeadings is not None:
                    for i, heading in enumerate(element.ColumnHeadings):  # Configure cols + headings
                        treeview.heading(heading, text=heading)
                        if element.AutoSizeColumns:
                            max_width = max_widths.get(i, 0)
                            max_width = max(max_width, len(heading))
                            width = min(element.MaxColumnWidth, max_width + 1)
                        else:
                            try:
                                width = element.ColumnWidths[i]
                            except:
                                width = element.DefaultColumnWidth
                        treeview.column(heading, width=width * _char_width_in_pixels(font) + 10, anchor=anchor)

                def add_treeview_data(node):
                    """

                    :param node:
                    :type node:

                    """
                    if node.key != '':
                        if node.icon:
                            if node.icon not in element.image_dict:
                                if type(node.icon) is bytes:
                                    photo = tk.PhotoImage(data=node.icon)
                                else:
                                    photo = tk.PhotoImage(file=node.icon)
                                element.image_dict[node.icon] = photo
                            else:
                                photo = element.image_dict.get(node.icon)

                            node.photo = photo
                            try:
                                id = treeview.insert(
                                    element.KeyToID[node.parent],
                                    'end',
                                    iid=None,
                                    text=node.text,
                                    values=node.values,
                                    open=element.ShowExpanded,
                                    image=node.photo,
                                )
                                element.IdToKey[id] = node.key
                                element.KeyToID[node.key] = id
                            except Exception as e:
                                print('Error inserting image into tree', e)
                        else:
                            id = treeview.insert(
                                element.KeyToID[node.parent],
                                'end',
                                iid=None,
                                text=node.text,
                                values=node.values,
                                open=element.ShowExpanded,
                            )
                            element.IdToKey[id] = node.key
                            element.KeyToID[node.key] = id

                    for node in node.children:
                        add_treeview_data(node)

                add_treeview_data(element.TreeData.root_node)
                treeview.column('#0', width=element.Col0Width * _char_width_in_pixels(font), anchor=tk.W)
                treeview.heading('#0', text=element.col0_heading)

                # ----- configure colors -----
                # style_name = str(element.Key) + '.Treeview'
                style_name = _make_ttk_style_name('.Treeview', element, primary_style=True)
                tree_style = ttk.Style()
                _change_ttk_theme(tree_style, toplevel_form.TtkTheme)

                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    tree_style.configure(style_name, background=element.BackgroundColor, fieldbackground=element.BackgroundColor)
                    if element.SelectedRowColors[1] is not None:
                        tree_style.map(
                            style_name,
                            background=_fixed_map(tree_style, style_name, 'background', element.SelectedRowColors),
                        )
                if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
                    tree_style.configure(style_name, foreground=element.TextColor)
                    if element.SelectedRowColors[0] is not None:
                        tree_style.map(
                            style_name,
                            foreground=_fixed_map(tree_style, style_name, 'foreground', element.SelectedRowColors),
                        )
                if element.HeaderTextColor is not None and element.HeaderTextColor != COLOR_SYSTEM_DEFAULT:
                    tree_style.configure(style_name + '.Heading', foreground=element.HeaderTextColor)
                if element.HeaderBackgroundColor is not None and element.HeaderBackgroundColor != COLOR_SYSTEM_DEFAULT:
                    tree_style.configure(style_name + '.Heading', background=element.HeaderBackgroundColor)
                if element.HeaderFont is not None:
                    tree_style.configure(style_name + '.Heading', font=element.HeaderFont)
                else:
                    tree_style.configure(style_name + '.Heading', font=font)
                if element.HeaderBorderWidth is not None:
                    tree_style.configure(style_name + '.Heading', borderwidth=element.HeaderBorderWidth)
                if element.HeaderRelief is not None:
                    tree_style.configure(style_name + '.Heading', relief=element.HeaderRelief)
                tree_style.configure(style_name, font=font)
                if element.RowHeight:
                    tree_style.configure(style_name, rowheight=element.RowHeight)
                else:
                    tree_style.configure(style_name, rowheight=_char_height_in_pixels(font))
                if element.BorderWidth is not None:
                    tree_style.configure(style_name, borderwidth=element.BorderWidth)

                treeview.configure(style=style_name)  # IMPORTANT! Be sure and set the style name for this widget

                if not element.HideVerticalScroll:
                    _make_ttk_scrollbar(element, 'v', toplevel_form)

                    element.Widget.configure(yscrollcommand=element.vsb.set)
                    element.vsb.pack(side=tk.RIGHT, fill='y')

                # Horizontal scrollbar
                if not element.VerticalScrollOnly:
                    # element.Widget.config(wrap='none')
                    _make_ttk_scrollbar(element, 'h', toplevel_form)
                    element.hsb.pack(side=tk.BOTTOM, fill='x')
                    element.Widget.configure(xscrollcommand=element.hsb.set)

                if not element.HideVerticalScroll or not element.VerticalScrollOnly:
                    element.Widget.bind('<Enter>', lambda event, em=element: testMouseHook(em))
                    element.Widget.bind('<Leave>', lambda event, em=element: testMouseUnhook(em))

                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)
                element.TKTreeview.pack(side=tk.LEFT, padx=0, pady=0, expand=expand, fill=fill)
                element_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=expand, fill=fill)
                if element.visible is False:
                    element._pack_forget_save_settings(alternate_widget=element.element_frame)  # seems like it should be the frame if following other elements conventions
                    # element.TKTreeview.pack_forget()
                treeview.bind('<<TreeviewSelect>>', element._treeview_selected)
                if element.Tooltip is not None:  # tooltip
                    element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

                if tclversion_detailed == '8.6.9' and ENABLE_TREEVIEW_869_PATCH:
                    # print('*** tk version 8.6.9 detected.... patching ttk treeview code ***')
                    tree_style.map(
                        style_name,
                        foreground=_fixed_map(tree_style, style_name, 'foreground', element.SelectedRowColors),
                        background=_fixed_map(tree_style, style_name, 'background', element.SelectedRowColors),
                    )

            # -------------------------  Separator placement element  ------------------------- #
            elif element_type == ELEM_TYPE_SEPARATOR:
                element = element  # type: VerticalSeparator
                # style_name = str(element.Key) + "Line.TSeparator"
                style_name = _make_ttk_style_name('.Line.TSeparator', element, primary_style=True)
                style = ttk.Style()

                _change_ttk_theme(style, toplevel_form.TtkTheme)

                if element.color not in (None, COLOR_SYSTEM_DEFAULT):
                    style.configure(style_name, background=element.color)
                separator = element.Widget = ttk.Separator(
                    tk_row_frame,
                    orient=element.Orientation,
                )

                expand, fill, row_should_expand, row_fill_direction = _add_expansion(element, row_should_expand, row_fill_direction)

                if element.Orientation.startswith('h'):
                    separator.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=tk.X, expand=True)
                else:
                    separator.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=tk.Y, expand=False)
                element.Widget.configure(style=style_name)  # IMPORTANT!  Apply the style
            # -------------------------  SizeGrip placement element  ------------------------- #
            elif element_type == ELEM_TYPE_SIZEGRIP:
                element = element  # type: Sizegrip
                style_name = 'Sizegrip.TSizegrip'
                style = ttk.Style()

                _change_ttk_theme(style, toplevel_form.TtkTheme)

                size_grip = element.Widget = ttk.Sizegrip(tk_row_frame)
                toplevel_form.sizegrip_widget = size_grip
                # if no size is specified, then use the background color for the window
                if element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    style.configure(style_name, background=element.BackgroundColor)
                else:
                    style.configure(style_name, background=toplevel_form.TKroot['bg'])
                size_grip.configure(style=style_name)

                size_grip.pack(side=tk.BOTTOM, anchor='se', padx=elementpad[0], pady=elementpad[1], fill=tk.X, expand=True)
                # tricky part of sizegrip... it shouldn't cause the row to expand, but should expand and should add X axis if
                # not already filling in that direction.  Otherwise, leaves things alone!
                # row_should_expand = True
                row_fill_direction = tk.BOTH if row_fill_direction in (tk.Y, tk.BOTH) else tk.X
            # -------------------------  StatusBar placement element  ------------------------- #
            elif element_type == ELEM_TYPE_STATUSBAR:
                # auto_size_text = element.AutoSizeText
                display_text = element.DisplayText  # text to display
                if auto_size_text is False:
                    width, height = element_size
                else:
                    lines = display_text.split('\n')
                    max_line_len = max([len(line) for line in lines])
                    num_lines = len(lines)
                    if max_line_len > element_size[0]:  # if text exceeds element size, the will have to wrap
                        width = element_size[0]
                    else:
                        width = max_line_len
                    height = num_lines
                # ---===--- LABEL widget create and place --- #
                stringvar = tk.StringVar()
                element.TKStringVar = stringvar
                stringvar.set(display_text)
                if auto_size_text:
                    width = 0
                if element.Justification is not None:
                    justification = element.Justification
                elif toplevel_form.TextJustification is not None:
                    justification = toplevel_form.TextJustification
                else:
                    justification = DEFAULT_TEXT_JUSTIFICATION
                justify = tk.LEFT if justification.startswith('l') else tk.CENTER if justification.startswith('c') else tk.RIGHT
                anchor = tk.NW if justification.startswith('l') else tk.N if justification.startswith('c') else tk.NE
                tktext_label = element.Widget = tk.Label(
                    tk_row_frame,
                    textvariable=stringvar,
                    width=width,
                    height=height,
                    justify=justify,
                    bd=border_depth,
                    font=font,
                )
                # Set wrap-length for text (in PIXELS) == PAIN IN THE ASS
                wraplen = tktext_label.winfo_reqwidth() + 40  # width of widget in Pixels
                if not auto_size_text and height == 1:
                    wraplen = 0
                # print("wraplen, width, height", wraplen, width, height)
                tktext_label.configure(anchor=anchor, wraplen=wraplen)  # set wrap to width of widget
                if element.Relief is not None:
                    tktext_label.configure(relief=element.Relief)
                if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
                    tktext_label.configure(background=element.BackgroundColor)
                if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
                    tktext_label.configure(fg=element.TextColor)
                tktext_label.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill=tk.X, expand=True)
                row_fill_direction = tk.X
                if element.visible is False:
                    element._pack_forget_save_settings()
                    # tktext_label.pack_forget()
                element.TKText = tktext_label
                if element.ClickSubmits:
                    tktext_label.bind('<Button-1>', element._TextClickedHandler)
                if element.Tooltip is not None:
                    element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
                _add_right_click_menu_and_grab(element)

        # ............................DONE WITH ROW pack the row of widgets ..........................#
        # done with row, pack the row of widgets

        anchor = 'nw'

        if row_justify.lower().startswith('c'):
            anchor = 'n'
        elif row_justify.lower().startswith('r'):
            anchor = 'ne'
        elif row_justify.lower().startswith('l'):
            anchor = 'nw'

        tk_row_frame.pack(side=tk.TOP, anchor=anchor, padx=0, pady=0, expand=row_should_expand, fill=row_fill_direction)
        if form.BackgroundColor is not None and form.BackgroundColor != COLOR_SYSTEM_DEFAULT:
            tk_row_frame.configure(background=form.BackgroundColor)

    return


def _get_hidden_master_root():
    """
    Creates the hidden master root window.  This window is never visible and represents the overall "application"
    """

    # if one is already made, then skip making another
    if Window.hidden_master_root is None:
        Window._IncrementOpenCount()
        Window.hidden_master_root = tk.Tk()
        Window.hidden_master_root.attributes('-alpha', 0)  # HIDE this window really really really
        # if not running_mac():
        try:
            Window.hidden_master_root.wm_overrideredirect(True)
        except Exception as e:
            if not running_mac():
                print('* Error performing wm_overrideredirect while hiding the hidden master root*', e)
        Window.hidden_master_root.withdraw()
    return Window.hidden_master_root


def _no_titlebar_setup(window):
    """
    Does the operations required to turn off the titlebar for the window.
    The Raspberry Pi required the settings to be make after the window's creation.
    Calling twice seems to have had better overall results so that's what's currently done.
    The MAC has been the problem with this feature.  It's been a chronic problem on the Mac.
    :param window:          window to turn off the titlebar if indicated in the settings
    :type window:           Window
    """
    try:
        if window.NoTitleBar:
            if running_linux():
                # window.TKroot.wm_attributes("-type", 'splash')
                window.TKroot.wm_attributes('-type', 'dock')
            else:
                window.TKroot.wm_overrideredirect(True)
                # Special case for Mac. Need to clear flag again if not tkinter version 8.6.10+
                # Previously restricted patch to only certain tkinter versions. Now use the patch setting exclusively regardless of tk ver
                # if running_mac() and ENABLE_MAC_NOTITLEBAR_PATCH and (sum([int(i) for i in tclversion_detailed.split('.')]) < 24):
                # if running_mac() and ENABLE_MAC_NOTITLEBAR_PATCH:
                if _mac_should_apply_notitlebar_patch():
                    print('* Applying Mac no_titlebar patch *')
                    window.TKroot.wm_overrideredirect(False)
    except Exception as e:
        warnings.warn(f'** Problem setting no titlebar {e} **', UserWarning)


def _convert_window_to_tk(window):
    """

    :type window: (Window)

    """
    master = window.TKroot
    master.title(window.Title)
    InitializeResults(window)

    PackFormIntoFrame(window, master, window)

    window.TKroot.configure(padx=window.Margins[0], pady=window.Margins[1])

    # ....................................... DONE creating and laying out window ..........................#
    if window._Size != (None, None):
        master.geometry('{}x{}'.format(window._Size[0], window._Size[1]))
    screen_width = master.winfo_screenwidth()  # get window info to move to middle of screen
    screen_height = master.winfo_screenheight()
    if window.Location is not None:
        if window.Location != (None, None):
            x, y = window.Location
        elif DEFAULT_WINDOW_LOCATION != (None, None):
            x, y = DEFAULT_WINDOW_LOCATION
        else:
            master.update_idletasks()  # don't forget to do updates or values are bad
            win_width = master.winfo_width()
            win_height = master.winfo_height()
            x = screen_width / 2 - win_width / 2
            y = screen_height / 2 - win_height / 2
            if y + win_height > screen_height:
                y = screen_height - win_height
            if x + win_width > screen_width:
                x = screen_width - win_width

        if window.RelativeLoction != (None, None):
            x += window.RelativeLoction[0]
            y += window.RelativeLoction[1]

        move_string = '+%i+%i' % (int(x), int(y))
        master.geometry(move_string)
        window.config_last_location = (int(x), (int(y)))
        window.TKroot.x = int(x)
        window.TKroot.y = int(y)
        window.starting_window_position = (int(x), (int(y)))
        master.update_idletasks()  # don't forget
        master.geometry(move_string)
        master.update_idletasks()  # don't forget
    else:
        master.update_idletasks()
        x, y = int(master.winfo_x()), int(master.winfo_y())
        window.config_last_location = x, y
        window.TKroot.x = x
        window.TKroot.y = y
        window.starting_window_position = x, y
    _no_titlebar_setup(window)

    return


# ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----#
def StartupTK(window):
    """
    NOT user callable
    Creates the window (for real) lays out all the elements, etc.  It's a HUGE set of things it does.  It's the basic
    "porting layer" that will change depending on the GUI framework PySimpleGUI is running on top of.

    :param window: you window object
    :type window:  (Window)

    """
    window = window  # type: Window
    # global _my_windows
    # ow = _my_windows.NumOpenWindows
    ow = Window.NumOpenWindows
    # print('Starting TK open Windows = {}'.format(ow))
    if ENABLE_TK_WINDOWS:
        root = tk.Tk()
    elif not ow and not window.ForceTopLevel:
        # if first window being created, make a throwaway, hidden master root.  This stops one user
        # window from becoming the child of another user window. All windows are children of this hidden window
        _get_hidden_master_root()
        root = tk.Toplevel(class_=window.Title)
    else:
        root = tk.Toplevel(class_=window.Title)
    if window.DebuggerEnabled:
        root.bind('<Cancel>', window._callback_main_debugger_window_create_keystroke)
        root.bind('<Pause>', window._callback_popout_window_create_keystroke)

    # If location is None, then there's no need to hide the window.  Let it build where it is going to end up being.
    if DEFAULT_HIDE_WINDOW_WHEN_CREATING is True and window.Location is not None:
        try:
            if not running_mac() or (running_mac() and not window.NoTitleBar) or (running_mac() and window.NoTitleBar and not _mac_should_apply_notitlebar_patch()):

                root.attributes('-alpha', 0)  # hide window while building it. makes for smoother 'paint'
        except Exception as e:
            print('*** Exception setting alpha channel to zero while creating window ***', e)

    if window.BackgroundColor is not None and window.BackgroundColor != COLOR_SYSTEM_DEFAULT:
        root.configure(background=window.BackgroundColor)
    Window._IncrementOpenCount()

    window.TKroot = root

    window._create_thread_queue()

    # for the Raspberry Pi. Need to set the attributes here, prior to the building of the window
    # so going ahead and doing it for all platforms, in addition to doing it after the window is packed
    # 2023-April - this call seems to be causing problems on MacOS 13.2.1 Ventura.  Input elements become non-responsive
    # if this call is made here and at the end of building the window
    if not running_mac():
        _no_titlebar_setup(window)

    if not window.Resizable:
        root.resizable(False, False)

    if window.DisableMinimize:
        root.attributes('-toolwindow', 1)

    if window.KeepOnTop:
        root.wm_attributes('-topmost', 1)

    if window.TransparentColor is not None:
        window.SetTransparentColor(window.TransparentColor)

    if window.scaling is not None:
        root.tk.call('tk', 'scaling', window.scaling)

    # root.protocol("WM_DELETE_WINDOW", MyFlexForm.DestroyedCallback())
    # root.bind('<Destroy>', MyFlexForm.DestroyedCallback())
    _convert_window_to_tk(window)

    # Make moveable window
    if window.GrabAnywhere is not False and not (window.NonBlocking and window.GrabAnywhere is not True):
        if not (ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR and running_mac() and not window.NoTitleBar):
            root.bind('<ButtonPress-1>', window._StartMoveGrabAnywhere)
            root.bind('<ButtonRelease-1>', window._StopMove)
            root.bind('<B1-Motion>', window._OnMotionGrabAnywhere)
    if window.GrabAnywhereUsingControlKey is not False and not (window.NonBlocking and window.GrabAnywhereUsingControlKey is not True):
        root.bind('<Control-Button-1>', window._StartMoveUsingControlKey)
        root.bind('<Control-ButtonRelease-1>', window._StopMove)
        root.bind('<Control-B1-Motion>', window._OnMotionUsingControlKey)
        # also enable movement using Control + Arrow key
        root.bind('<Control-Left>', window._move_callback)
        root.bind('<Control-Right>', window._move_callback)
        root.bind('<Control-Up>', window._move_callback)
        root.bind('<Control-Down>', window._move_callback)

    window.set_icon(window.WindowIcon)
    try:
        alpha_channel = 1 if window.AlphaChannel is None else window.AlphaChannel
        root.attributes('-alpha', alpha_channel)  # Make window visible again
    except Exception as e:
        print(f'**** Error setting Alpha Channel to {alpha_channel} after window was created ****', e)
        # pass

    if window.ReturnKeyboardEvents and not window.NonBlocking:
        root.bind('<KeyRelease>', window._KeyboardCallback)
        root.bind('<MouseWheel>', window._MouseWheelCallback)
        root.bind('<Button-4>', window._MouseWheelCallback)
        root.bind('<Button-5>', window._MouseWheelCallback)
    elif window.ReturnKeyboardEvents:
        root.bind('<Key>', window._KeyboardCallback)
        root.bind('<MouseWheel>', window._MouseWheelCallback)
        root.bind('<Button-4>', window._MouseWheelCallback)
        root.bind('<Button-5>', window._MouseWheelCallback)

    DEFAULT_WINDOW_SNAPSHOT_KEY_CODE = main_global_get_screen_snapshot_symcode()

    if DEFAULT_WINDOW_SNAPSHOT_KEY_CODE:
        # print('**** BINDING THE SNAPSHOT!', DEFAULT_WINDOW_SNAPSHOT_KEY_CODE, DEFAULT_WINDOW_SNAPSHOT_KEY)
        window.bind(DEFAULT_WINDOW_SNAPSHOT_KEY_CODE, DEFAULT_WINDOW_SNAPSHOT_KEY, propagate=False)
        # window.bind('<Win_L><F12>', DEFAULT_WINDOW_SNAPSHOT_KEY, )

    if window.NoTitleBar:
        window.TKroot.focus_force()

    if window.AutoClose:
        # if the window is being finalized, then don't start the autoclose timer
        if not window.finalize_in_progress:
            window._start_autoclose_timer()
            # duration = DEFAULT_AUTOCLOSE_TIME if window.AutoCloseDuration is None else window.AutoCloseDuration
            # window.TKAfterID = root.after(int(duration * 1000), window._AutoCloseAlarmCallback)

    if window.Timeout is not None:
        window.TKAfterID = root.after(int(window.Timeout), window._TimeoutAlarmCallback)
    if window.NonBlocking:
        window.TKroot.protocol('WM_DESTROY_WINDOW', window._OnClosingCallback)
        window.TKroot.protocol('WM_DELETE_WINDOW', window._OnClosingCallback)

    else:  # it's a blocking form
        # print('..... CALLING MainLoop')
        window.CurrentlyRunningMainloop = True
        window.TKroot.protocol('WM_DESTROY_WINDOW', window._OnClosingCallback)
        window.TKroot.protocol('WM_DELETE_WINDOW', window._OnClosingCallback)

        if window.modal or DEFAULT_MODAL_WINDOWS_FORCED:
            window.make_modal()

        if window.enable_window_config_events:
            window.TKroot.bind('<Configure>', window._config_callback)

        # ----------------------------------- tkinter mainloop call -----------------------------------
        Window._window_running_mainloop = window
        Window._root_running_mainloop = window.TKroot
        window.TKroot.mainloop()
        window.CurrentlyRunningMainloop = False
        window.TimerCancelled = True
        # print('..... BACK from MainLoop')
        if not window.FormRemainedOpen:
            Window._DecrementOpenCount()
            # _my_windows.Decrement()
        if window.RootNeedsDestroying:
            try:
                window.TKroot.destroy()
            except:
                pass
            window.RootNeedsDestroying = False
    return


def _set_icon_for_tkinter_window(root, icon=None, pngbase64=None):
    """
    At the moment, this function is only used by the get_filename or folder with the no_window option set.
    Changes the icon that is shown on the title bar and on the task bar.
    NOTE - The file type is IMPORTANT and depends on the OS!
    Can pass in:
    * filename which must be a .ICO icon file for windows, PNG file for Linux
    * bytes object
    * BASE64 encoded file held in a variable

    :param root:      The window being modified
    :type root:       (tk.Tk or tk.TopLevel)
    :param icon:      Filename or bytes object
    :type icon:       (str | bytes)
    :param pngbase64: Base64 encoded image
    :type pngbase64:  (bytes)
    """

    if type(icon) is bytes or pngbase64 is not None:
        wicon = tkinter.PhotoImage(data=icon if icon is not None else pngbase64)
        try:
            root.tk.call('wm', 'iconphoto', root._w, wicon)
        except:
            wicon = tkinter.PhotoImage(data=DEFAULT_BASE64_ICON)
            try:
                root.tk.call('wm', 'iconphoto', root._w, wicon)
            except Exception as e:
                print('Set icon exception', e)
                pass
        return

    wicon = icon
    try:
        root.iconbitmap(icon)
    except Exception:
        try:
            wicon = tkinter.PhotoImage(file=icon)
            root.tk.call('wm', 'iconphoto', root._w, wicon)
        except Exception as e:
            try:
                wicon = tkinter.PhotoImage(data=DEFAULT_BASE64_ICON)
                try:
                    root.tk.call('wm', 'iconphoto', root._w, wicon)
                except Exception as e:
                    print('Set icon exception', e)
                    pass
            except:
                print('Set icon exception', e)
                pass


# ==============================_GetNumLinesNeeded ==#
# Helper function for determining how to wrap text   #
# ===================================================#
def _GetNumLinesNeeded(text, max_line_width):
    if max_line_width == 0:
        return 1
    lines = text.split('\n')
    lines_used = []
    for L in lines:
        lines_used.append(len(L) // max_line_width + (len(L) % max_line_width > 0))  # fancy math to round up
    total_lines_needed = sum(lines_used)
    return total_lines_needed


# ==============================  PROGRESS METER ========================================== #


def convert_args_to_single_string(*args):
    """

    :param *args:
    :type *args:

    """
    (
        max_line_total,
        width_used,
        total_lines,
    ) = (
        0,
        0,
        0,
    )
    single_line_message = ''
    # loop through args and built a SINGLE string from them
    for message in args:
        # fancy code to check if string and convert if not is not need. Just always convert to string :-)
        # if not isinstance(message, str): message = str(message)
        message = str(message)
        longest_line_len = max([len(line) for line in message.split('\n')])
        width_used = max(longest_line_len, width_used)
        max_line_total = max(max_line_total, width_used)
        lines_needed = _GetNumLinesNeeded(message, width_used)
        total_lines += lines_needed
        single_line_message += message + '\n'
    return single_line_message, width_used, total_lines


METER_REASON_CANCELLED = 'cancelled'
METER_REASON_CLOSED = 'closed'
METER_REASON_REACHED_MAX = 'finished'
METER_OK = True
METER_STOPPED = False


class _QuickMeter:
    active_meters = {}
    exit_reasons = {}

    def __init__(
        self,
        title,
        current_value,
        max_value,
        key,
        *args,
        orientation='v',
        bar_color=(None, None),
        button_color=(None, None),
        size=DEFAULT_PROGRESS_BAR_SIZE,
        border_width=None,
        grab_anywhere=False,
        no_titlebar=False,
        keep_on_top=None,
        no_button=False,
    ):
        """

        :param title:         text to display in element
        :type title:          (str)
        :param current_value: current value
        :type current_value:  (int)
        :param max_value:     max value of progress meter
        :type max_value:      (int)
        :param key:           Used with window.find_element and with return values to uniquely identify this element
        :type key:            str | int | tuple | object
        :param *args:         stuff to output
        :type *args:          (Any)
        :param orientation:   'horizontal' or 'vertical' ('h' or 'v' work) (Default value = 'vertical' / 'v')
        :type orientation:    (str)
        :param bar_color:     The 2 colors that make up a progress bar. Either a tuple of 2 strings or a string. Tuple - (bar, background). A string with 1 color changes the background of the bar only. A string with 2 colors separated by "on" like "red on blue" specifies a red bar on a blue background.
        :type bar_color:      (str, str) or str
        :param button_color:  button color (foreground, background)
        :type button_color:   (str, str) | str
        :param size:          (w,h) w=characters-wide, h=rows-high (Default value = DEFAULT_PROGRESS_BAR_SIZE)
        :type size:           (int, int)
        :param border_width:  width of border around element
        :type border_width:   (int)
        :param grab_anywhere: If True: can grab anywhere to move the window (Default = False)
        :type grab_anywhere:  (bool)
        :param no_titlebar:   If True: window will be created without a titlebar
        :type no_titlebar:    (bool)
        :param keep_on_top:   If True the window will remain above all current windows
        :type keep_on_top:    (bool)
        :param no_button:     If True: window will be created without a cancel button
        :type no_button:      (bool)
        """
        self.start_time = datetime.datetime.utcnow()
        self.key = key
        self.orientation = orientation
        self.bar_color = bar_color
        self.size = size
        self.grab_anywhere = grab_anywhere
        self.button_color = button_color
        self.border_width = border_width
        self.no_titlebar = no_titlebar
        self.title = title
        self.current_value = current_value
        self.max_value = max_value
        self.close_reason = None
        self.keep_on_top = keep_on_top
        self.no_button = no_button
        self.window = self.BuildWindow(*args)

    def BuildWindow(self, *args):
        layout = []
        if self.orientation.lower().startswith('h'):
            col = []
            col += [[T(''.join(map(lambda x: str(x) + '\n', args)), key='-OPTMSG-')]]  # convert all *args into one string that can be updated

            col += [
                [T('', size=(30, 10), key='-STATS-')],
                [
                    ProgressBar(
                        max_value=self.max_value,
                        orientation='h',
                        key='-PROG-',
                        size=self.size,
                        bar_color=self.bar_color,
                    )
                ],
            ]
            if not self.no_button:
                col += [[Cancel(button_color=self.button_color), Stretch()]]
            layout = [Column(col)]
        else:
            col = [
                [
                    ProgressBar(
                        max_value=self.max_value,
                        orientation='v',
                        key='-PROG-',
                        size=self.size,
                        bar_color=self.bar_color,
                    )
                ]
            ]
            col2 = []

            col2 += [[T(''.join(map(lambda x: str(x) + '\n', args)), key='-OPTMSG-')]]  # convert all *args into one string that can be updated

            col2 += [[T('', size=(30, 10), key='-STATS-')]]

            if not self.no_button:
                col2 += [[Cancel(button_color=self.button_color), Stretch()]]

            layout = [Column(col), Column(col2)]
        self.window = Window(
            self.title,
            grab_anywhere=self.grab_anywhere,
            border_depth=self.border_width,
            no_titlebar=self.no_titlebar,
            disable_close=True,
            keep_on_top=self.keep_on_top,
        )
        self.window.Layout([layout]).Finalize()

        return self.window

    def UpdateMeter(self, current_value, max_value, *args):  # support for *args when updating

        self.current_value = current_value
        self.max_value = max_value
        self.window.Element('-PROG-').UpdateBar(self.current_value, self.max_value)
        self.window.Element('-STATS-').Update('\n'.join(self.ComputeProgressStats()))
        self.window.Element('-OPTMSG-').Update(value=''.join(map(lambda x: str(x) + '\n', args)))  # update the string with the args
        event, values = self.window.read(timeout=0)
        if event in ('Cancel', None) or current_value >= max_value:
            exit_reason = METER_REASON_CANCELLED if event in ('Cancel', None) else METER_REASON_REACHED_MAX if current_value >= max_value else METER_STOPPED
            self.window.close()
            del _QuickMeter.active_meters[self.key]
            _QuickMeter.exit_reasons[self.key] = exit_reason
            return _QuickMeter.exit_reasons[self.key]
        return METER_OK

    def ComputeProgressStats(self):
        utc = datetime.datetime.utcnow()
        time_delta = utc - self.start_time
        total_seconds = time_delta.total_seconds()
        if not total_seconds:
            total_seconds = 1
        try:
            time_per_item = total_seconds / self.current_value
        except:
            time_per_item = 1
        seconds_remaining = (self.max_value - self.current_value) * time_per_item
        time_remaining = str(datetime.timedelta(seconds=seconds_remaining))
        time_remaining_short = (time_remaining).split('.')[0]
        time_delta_short = str(time_delta).split('.')[0]
        total_time = time_delta + datetime.timedelta(seconds=seconds_remaining)
        total_time_short = str(total_time).split('.')[0]
        self.stat_messages = [
            f'{self.current_value} of {self.max_value}',
            f'{100 * self.current_value // self.max_value} %',
            '',
            f' {self.current_value / total_seconds:6.2f} Iterations per Second',
            f' {total_seconds / (self.current_value if self.current_value else 1):6.2f} Seconds per Iteration',
            '',
            f'{time_delta_short} Elapsed Time',
            f'{time_remaining_short} Time Remaining',
            f'{total_time_short} Estimated Total Time',
        ]
        return self.stat_messages


def one_line_progress_meter(
    title,
    current_value,
    max_value,
    *args,
    key='OK for 1 meter',
    orientation='v',
    bar_color=(None, None),
    button_color=None,
    size=DEFAULT_PROGRESS_BAR_SIZE,
    border_width=None,
    grab_anywhere=False,
    no_titlebar=False,
    keep_on_top=None,
    no_button=False,
):
    """
    :param title:         text to display in titlebar of window
    :type title:          (str)
    :param current_value: current value
    :type current_value:  (int)
    :param max_value:     max value of progress meter
    :type max_value:      (int)
    :param *args:         stuff to output as text in the window along with the meter
    :type *args:          (Any)
    :param key:           Used to differentiate between multiple meters. Used to cancel meter early. Now optional as there is a default value for single meters
    :type key:            str | int | tuple | object
    :param orientation:   'horizontal' or 'vertical' ('h' or 'v' work) (Default value = 'vertical' / 'v')
    :type orientation:    (str)
    :param bar_color:     The 2 colors that make up a progress bar. Either a tuple of 2 strings or a string. Tuple - (bar, background). A string with 1 color changes the background of the bar only. A string with 2 colors separated by "on" like "red on blue" specifies a red bar on a blue background.
    :type bar_color:      (str, str) or str
    :param button_color:  button color (foreground, background)
    :type button_color:   (str, str) | str
    :param size:          (w,h) w=characters-wide, h=rows-high (Default value = DEFAULT_PROGRESS_BAR_SIZE)
    :type size:           (int, int)
    :param border_width:  width of border around element
    :type border_width:   (int)
    :param grab_anywhere: If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:  (bool)
    :param no_titlebar:   If True: no titlebar will be shown on the window
    :type no_titlebar:    (bool)
    :param keep_on_top:   If True the window will remain above all current windows
    :type keep_on_top:    (bool)
    :param no_button:     If True: window will be created without a cancel button
    :type no_button:      (bool)
    :return:              True if updated successfully. False if user closed the meter with the X or Cancel button
    :rtype:               (bool)
    """
    if key not in _QuickMeter.active_meters:
        meter = _QuickMeter(
            title,
            current_value,
            max_value,
            key,
            *args,
            orientation=orientation,
            bar_color=bar_color,
            button_color=button_color,
            size=size,
            border_width=border_width,
            grab_anywhere=grab_anywhere,
            no_titlebar=no_titlebar,
            keep_on_top=keep_on_top,
            no_button=no_button,
        )
        _QuickMeter.active_meters[key] = meter
        _QuickMeter.exit_reasons[key] = None

    else:
        meter = _QuickMeter.active_meters[key]

    meter.UpdateMeter(current_value, max_value, *args)  # pass the *args to UpdateMeter function

    # XXX: wtf?
    one_line_progress_meter.exit_reasons = getattr(one_line_progress_meter, 'exit_reasons', _QuickMeter.exit_reasons)
    exit_reason = one_line_progress_meter.exit_reasons.get(key)

    return METER_OK if exit_reason in (None, METER_REASON_REACHED_MAX) else METER_STOPPED


def one_line_progress_meter_cancel(key='OK for 1 meter'):
    """
    Cancels and closes a previously created One Line Progress Meter window

    :param key: Key used when meter was created
    :type key:  (Any)
    :return:    None
    :rtype:     None
    """
    try:
        meter = _QuickMeter.active_meters[key]
        meter.window.Close()
        del _QuickMeter.active_meters[key]
        _QuickMeter.exit_reasons[key] = METER_REASON_CANCELLED
    except:  # meter is already deleted
        return


def get_complimentary_hex(color):
    """
    :param color: color string, like "#RRGGBB"
    :type color:  (str)
    :return:      color string, like "#RRGGBB"
    :rtype:       (str)
    """

    # strip the # from the beginning
    color = color[1:]
    # convert the string into hex
    color = int(color, 16)
    # invert the three bytes
    # as good as substracting each of RGB component by 255(FF)
    comp_color = 0xFFFFFF ^ color
    # convert the color back to hex by prefixing a #
    comp_color = '#%06X' % comp_color
    return comp_color


# ========================  EasyPrint           =====#
# ===================================================#
class _DebugWin:
    debug_window = None

    def __init__(
        self,
        size=(None, None),
        location=(None, None),
        relative_location=(None, None),
        font=None,
        no_titlebar=False,
        no_button=False,
        grab_anywhere=False,
        keep_on_top=None,
        do_not_reroute_stdout=True,
        echo_stdout=False,
        resizable=True,
        blocking=False,
    ):
        """

        :param size:                  (w,h) w=characters-wide, h=rows-high
        :type size:                   (int, int)
        :param location:              Location of upper left corner of the window
        :type location:               (int, int)
        :param relative_location:     (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
        :type relative_location:      (int, int)
        :param font:                  specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
        :type font:                   (str or (str, int[, str]) or None)
        :param no_titlebar:           If True no titlebar will be shown
        :type no_titlebar:            (bool)
        :param no_button:             show button
        :type no_button:              (bool)
        :param grab_anywhere:         If True: can grab anywhere to move the window (Default = False)
        :type grab_anywhere:          (bool)
        :param location:              Location of upper left corner of the window
        :type location:               (int, int)
        :param do_not_reroute_stdout: bool value
        :type do_not_reroute_stdout:  (bool)
        :param echo_stdout:           If True stdout is sent to both the console and the debug window
        :type echo_stdout:            (bool)
        :param resizable:             if True, makes the window resizble
        :type resizable:              (bool)
        :param blocking:              if True, makes the window block instead of returning immediately
        :type blocking:               (bool)
        """

        # Show a form that's a running counter
        self.size = size
        self.location = location
        self.relative_location = relative_location
        self.font = font
        self.no_titlebar = no_titlebar
        self.no_button = no_button
        self.grab_anywhere = grab_anywhere
        self.keep_on_top = keep_on_top
        self.do_not_reroute_stdout = do_not_reroute_stdout
        self.echo_stdout = echo_stdout
        self.resizable = resizable
        self.blocking = blocking

        win_size = size if size != (None, None) else DEFAULT_DEBUG_WINDOW_SIZE
        self.output_element = Multiline(
            size=win_size,
            autoscroll=True,
            auto_refresh=True,
            reroute_stdout=False if do_not_reroute_stdout else True,
            echo_stdout_stderr=self.echo_stdout,
            reroute_stderr=False if do_not_reroute_stdout else True,
            expand_x=True,
            expand_y=True,
            key='-MULTILINE-',
        )
        if no_button:
            self.layout = [[self.output_element]]
        else:
            if blocking:
                self.quit_button = Button('Quit', key='Quit')
            else:
                self.quit_button = DummyButton('Quit', key='Quit')
            self.layout = [[self.output_element], [pin(self.quit_button), pin(B('Pause', key='-PAUSE-')), Stretch()]]

        self.layout[-1] += [Sizegrip()]

        self.window = Window(
            'Debug Window',
            self.layout,
            no_titlebar=no_titlebar,
            auto_size_text=True,
            location=location,
            relative_location=relative_location,
            font=font or ('Courier New', 10),
            grab_anywhere=grab_anywhere,
            keep_on_top=keep_on_top,
            finalize=True,
            resizable=resizable,
        )
        return

    def reopen_window(self):
        if self.window is None or (self.window is not None and self.window.is_closed()):
            self.__init__(
                size=self.size,
                location=self.location,
                relative_location=self.relative_location,
                font=self.font,
                no_titlebar=self.no_titlebar,
                no_button=self.no_button,
                grab_anywhere=self.grab_anywhere,
                keep_on_top=self.keep_on_top,
                do_not_reroute_stdout=self.do_not_reroute_stdout,
                resizable=self.resizable,
                echo_stdout=self.echo_stdout,
            )

    def Print(
        self,
        *args,
        end=None,
        sep=None,
        text_color=None,
        background_color=None,
        erase_all=False,
        font=None,
        blocking=None,
    ):
        global SUPPRESS_WIDGET_NOT_FINALIZED_WARNINGS
        suppress = SUPPRESS_WIDGET_NOT_FINALIZED_WARNINGS
        SUPPRESS_WIDGET_NOT_FINALIZED_WARNINGS = True
        sepchar = sep if sep is not None else ' '
        endchar = end if end is not None else '\n'
        self.reopen_window()  # if needed, open the window again

        timeout = 0 if not blocking else None
        if erase_all:
            self.output_element.update('')

        if self.do_not_reroute_stdout:
            end_str = str(end) if end is not None else '\n'
            sep_str = str(sep) if sep is not None else ' '

            outstring = ''
            num_args = len(args)
            for i, arg in enumerate(args):
                outstring += str(arg)
                if i != num_args - 1:
                    outstring += sep_str
            outstring += end_str
            try:
                self.output_element.update(
                    outstring,
                    append=True,
                    text_color_for_value=text_color,
                    background_color_for_value=background_color,
                    font_for_value=font,
                )
            except:
                self.window = None
                self.reopen_window()
                self.output_element.update(
                    outstring,
                    append=True,
                    text_color_for_value=text_color,
                    background_color_for_value=background_color,
                    font_for_value=font,
                )

        else:
            print(*args, sep=sepchar, end=endchar)
        # This is tricky....changing the button type depending on the blocking parm. If blocking, then the "Quit" button should become a normal button
        if blocking and not self.no_button:
            self.quit_button.BType = BUTTON_TYPE_READ_FORM
            try:  # The window may be closed by user at any time, so have to protect
                self.quit_button.update(text='Click to continue...')
            except:
                self.window = None
        elif not self.no_button:
            self.quit_button.BType = BUTTON_TYPE_CLOSES_WIN_ONLY
            try:  # The window may be closed by user at any time, so have to protect
                self.quit_button.update(text='Quit')
            except:
                self.window = None

        try:  # The window may be closed by user at any time, so have to protect
            if blocking and not self.no_button:
                self.window['-PAUSE-'].update(visible=False)
            elif not self.no_button:
                self.window['-PAUSE-'].update(visible=True)
        except:
            self.window = None

        self.reopen_window()  # if needed, open the window again

        paused = None
        while True:
            event, values = self.window.read(timeout=timeout)

            if event == WIN_CLOSED:
                self.Close()
                break
            elif blocking and event == 'Quit':
                break
            elif not paused and event == TIMEOUT_EVENT and not blocking:
                break
            elif event == '-PAUSE-':
                if blocking or self.no_button:  # if blocking or shouldn't have been a button event, ignore the pause button entirely
                    continue
                if paused:
                    self.window['-PAUSE-'].update(text='Pause')
                    self.quit_button.update(visible=True)
                    break
                paused = True
                self.window['-PAUSE-'].update(text='Resume')
                self.quit_button.update(visible=False)
                timeout = None

        SUPPRESS_WIDGET_NOT_FINALIZED_WARNINGS = suppress

    def Close(self):
        if self.window.XFound:  # increment the number of open windows to get around a bug with debug windows
            Window._IncrementOpenCount()
        self.window.close()
        self.window = None


def easy_print(
    *args,
    size=(None, None),
    end=None,
    sep=None,
    location=(None, None),
    relative_location=(None, None),
    font=None,
    no_titlebar=False,
    no_button=False,
    grab_anywhere=False,
    keep_on_top=None,
    do_not_reroute_stdout=True,
    echo_stdout=False,
    text_color=None,
    background_color=None,
    colors=None,
    c=None,
    erase_all=False,
    resizable=True,
    blocking=None,
    wait=None,
):
    """
    Works like a "print" statement but with windowing options.  Routes output to the "Debug Window"

    In addition to the normal text and background colors, you can use a "colors" tuple/string
    The "colors" or "c" parameter defines both the text and background in a single parm.
    It can be a tuple or a single single. Both text and background colors need to be specified
    colors -(str, str) or str.  A combined text/background color definition in a single parameter
    c - (str, str) - Colors tuple has format (foreground, backgrouned)
    c - str - can also be a string of the format "foreground on background"  ("white on red")

    :param *args:                 stuff to output
    :type *args:                  (Any)
    :param size:                  (w,h) w=characters-wide, h=rows-high
    :type size:                   (int, int)
    :param end:                   end character
    :type end:                    (str)
    :param sep:                   separator character
    :type sep:                    (str)
    :param location:              Location of upper left corner of the window
    :type location:               (int, int)
    :param relative_location:     (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:      (int, int)
    :param font:                  specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                   (str or (str, int[, str]) or None)
    :param no_titlebar:           If True no titlebar will be shown
    :type no_titlebar:            (bool)
    :param no_button:             don't show button
    :type no_button:              (bool)
    :param grab_anywhere:         If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:          (bool)
    :param background_color:      color of background
    :type background_color:       (str)
    :param text_color:            color of the text
    :type text_color:             (str)
    :param keep_on_top:           If True the window will remain above all current windows
    :type keep_on_top:            (bool)
    :param location:              Location of upper left corner of the window
    :type location:               (int, int)
    :param do_not_reroute_stdout: do not reroute stdout and stderr. If False, both stdout and stderr will reroute to here
    :type do_not_reroute_stdout:  (bool)
    :param echo_stdout:           If True stdout is sent to both the console and the debug window
    :type echo_stdout:            (bool)
    :param colors:                Either a tuple or a string that has both the text and background colors
    :type colors:                 (str) or (str, str)
    :param c:                     Either a tuple or a string that has both the text and background colors
    :type c:                      (str) or (str, str)
    :param resizable:             if True, the user can resize the debug window. Default is True
    :type resizable:              (bool)
    :param erase_all:             If True when erase the output before printing
    :type erase_all:              (bool)
    :param blocking:              if True, makes the window block instead of returning immediately. The "Quit" button changers to "More"
    :type blocking:               (bool | None)
    :param wait:                  Same as the "blocking" parm. It's an alias.  if True, makes the window block instead of returning immediately. The "Quit" button changes to "Click to Continue..."
    :type wait:                   (bool | None)
    :return:
    :rtype:
    """

    blocking = blocking or wait
    if _DebugWin.debug_window is None:
        _DebugWin.debug_window = _DebugWin(
            size=size,
            location=location,
            relative_location=relative_location,
            font=font,
            no_titlebar=no_titlebar,
            no_button=no_button,
            grab_anywhere=grab_anywhere,
            keep_on_top=keep_on_top,
            do_not_reroute_stdout=do_not_reroute_stdout,
            echo_stdout=echo_stdout,
            resizable=resizable,
            blocking=blocking,
        )
    txt_color, bg_color = _parse_colors_parm(c or colors)
    _DebugWin.debug_window.Print(
        *args,
        end=end,
        sep=sep,
        text_color=text_color or txt_color,
        background_color=background_color or bg_color,
        erase_all=erase_all,
        font=font,
        blocking=blocking,
    )


def easy_print_close():
    """
    Close a previously opened EasyPrint window

    :return:
    :rtype:
    """
    if _DebugWin.debug_window is not None:
        _DebugWin.debug_window.Close()
        _DebugWin.debug_window = None


#                            d8b          888
#                            Y8P          888
#                                         888
#   .d8888b 88888b.  888d888 888 88888b.  888888
#  d88P"    888 "88b 888P"   888 888 "88b 888
#  888      888  888 888     888 888  888 888
#  Y88b.    888 d88P 888     888 888  888 Y88b.
#   "Y8888P 88888P"  888     888 888  888  "Y888
#           888
#           888
#           888


CPRINT_DESTINATION_WINDOW = None
CPRINT_DESTINATION_MULTILINE_ELMENT_KEY = None


def cprint_set_output_destination(window, multiline_key):
    """
    Sets up the color print (cprint) output destination
    :param window:        The window that the cprint call will route the output to
    :type window:         (Window)
    :param multiline_key: Key for the Multiline Element where output will be sent
    :type multiline_key:  (Any)
    :return:              None
    :rtype:               None
    """
    global CPRINT_DESTINATION_WINDOW, CPRINT_DESTINATION_MULTILINE_ELMENT_KEY

    CPRINT_DESTINATION_WINDOW = window
    CPRINT_DESTINATION_MULTILINE_ELMENT_KEY = multiline_key


def cprint(
    *args,
    end=None,
    sep=' ',
    text_color=None,
    font=None,
    t=None,
    background_color=None,
    b=None,
    colors=None,
    c=None,
    window=None,
    key=None,
    justification=None,
    autoscroll=True,
    erase_all=False,
):
    """
    Color print to a multiline element in a window of your choice.
    Must have EITHER called cprint_set_output_destination prior to making this call so that the
    window and element key can be saved and used here to route the output, OR used the window
    and key parameters to the cprint function to specicy these items.

    args is a variable number of things you want to print.

    end - The end char to use just like print uses
    sep - The separation character like print uses
    text_color - The color of the text
            key - overrides the previously defined Multiline key
    window - overrides the previously defined window to output to
    background_color - The color of the background
    colors -(str, str) or str.  A combined text/background color definition in a single parameter

    There are also "aliases" for text_color, background_color and colors (t, b, c)
     t - An alias for color of the text (makes for shorter calls)
    b - An alias for the background_color parameter
    c - (str, str) - "shorthand" way of specifying color. (foreground, backgrouned)
    c - str - can also be a string of the format "foreground on background"  ("white on red")

    With the aliases it's possible to write the same print but in more compact ways:
    cprint('This will print white text on red background', c=('white', 'red'))
    cprint('This will print white text on red background', c='white on red')
    cprint('This will print white text on red background', text_color='white', background_color='red')
    cprint('This will print white text on red background', t='white', b='red')

    :param *args:            stuff to output
    :type *args:             (Any)
    :param text_color:       Color of the text
    :type text_color:        (str)
    :param font:             specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike for the value being updated
    :type font:              (str or (str, int[, str]) or None)
    :param background_color: The background color of the line
    :type background_color:  (str)
    :param colors:           Either a tuple or a string that has both the text and background colors "text on background" or just the text color
    :type colors:            (str) or (str, str)
    :param t:                Color of the text
    :type t:                 (str)
    :param b:                The background color of the line
    :type b:                 (str)
    :param c:                Either a tuple or a string.  Same as the color parm
    :type c:                 (str) or (str, str)
    :param end:              end character
    :type end:               (str)
    :param sep:              separator character
    :type sep:               (str)
    :param key:              key of multiline to output to (if you want to override the one previously set)
    :type key:               (Any)
    :param window:           Window containing the multiline to output to (if you want to override the one previously set)
    :type window:            (Window)
    :param justification:    text justification. left, right, center. Can use single characters l, r, c. Sets only for this value, not entire element
    :type justification:     (str)
    :param autoscroll:       If True the contents of the element will automatically scroll as more data added to the end
    :type autoscroll:        (bool)
    :param erase_all         If True the contents of the element will be cleared before printing happens
    :type erase_all          (bool)
    """

    destination_key = CPRINT_DESTINATION_MULTILINE_ELMENT_KEY if key is None else key
    destination_window = window or CPRINT_DESTINATION_WINDOW

    if (destination_window is None and window is None) or (destination_key is None and key is None):
        print(
            '** Warning ** Attempting to perform a cprint without a valid window & key',
            'Will instead print on Console',
            'You can specify window and key in this cprint call, or set ahead of time using cprint_set_output_destination',
        )
        print(*args)
        return

    kw_text_color = text_color or t
    kw_background_color = background_color or b
    dual_color = colors or c
    try:
        if isinstance(dual_color, tuple):
            kw_text_color = dual_color[0]
            kw_background_color = dual_color[1]
        elif isinstance(dual_color, str):
            if ' on ' in dual_color:  # if has "on" in the string, then have both text and background
                kw_text_color = dual_color.split(' on ')[0]
                kw_background_color = dual_color.split(' on ')[1]
            else:  # if no "on" then assume the color string is just the text color
                kw_text_color = dual_color
    except Exception as e:
        print('* cprint warning * you messed up with color formatting', e)

    mline = destination_window.find_element(destination_key, silent_on_error=True)  # type: Multiline
    try:
        # mline = destination_window[destination_key]     # type: Multiline
        if erase_all is True:
            mline.update('')
        if end is None:
            mline.print(
                *args,
                text_color=kw_text_color,
                background_color=kw_background_color,
                end='',
                sep=sep,
                justification=justification,
                font=font,
                autoscroll=autoscroll,
            )
            mline.print('', justification=justification, autoscroll=autoscroll)
        else:
            mline.print(
                *args,
                text_color=kw_text_color,
                background_color=kw_background_color,
                end=end,
                sep=sep,
                justification=justification,
                font=font,
                autoscroll=autoscroll,
            )
    except Exception as e:
        print('** cprint error trying to print to the multiline. Printing to console instead **', e)
        print(*args, end=end, sep=sep)


# ------------------------------------------------------------------------------------------------ #
# A print-like call that can be used to output to a multiline element as if it's an Output element #
# ------------------------------------------------------------------------------------------------ #


def _print_to_element(
    multiline_element,
    *args,
    end=None,
    sep=None,
    text_color=None,
    background_color=None,
    autoscroll=None,
    justification=None,
    font=None,
):
    """
    Print like Python normally prints except route the output to a multiline element and also add colors if desired

    :param multiline_element: The multiline element to be output to
    :type multiline_element:  (Multiline)
    :param args:              The arguments to print
    :type args:               List[Any]
    :param end:               The end char to use just like print uses
    :type end:                (str)
    :param sep:               The separation character like print uses
    :type sep:                (str)
    :param text_color:        color of the text
    :type text_color:         (str)
    :param background_color:  The background color of the line
    :type background_color:   (str)
    :param autoscroll:        If True (the default), the element will scroll to bottom after updating
    :type autoscroll:         (bool)
    :param font:              specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike for the value being updated
    :type font:               str | (str, int)
    """
    end_str = str(end) if end is not None else '\n'
    sep_str = str(sep) if sep is not None else ' '

    outstring = ''
    num_args = len(args)
    for i, arg in enumerate(args):
        outstring += str(arg)
        if i != num_args - 1:
            outstring += sep_str
    outstring += end_str

    multiline_element.update(
        outstring,
        append=True,
        text_color_for_value=text_color,
        background_color_for_value=background_color,
        autoscroll=autoscroll,
        justification=justification,
        font_for_value=font,
    )

    try:  # if the element is set to autorefresh, then refresh the parent window
        if multiline_element.AutoRefresh:
            multiline_element.ParentForm.refresh()
    except:
        pass


def _parse_colors_parm(colors):
    """
    Parse a colors parameter into its separate colors.
    Some functions accept a dual colors string/tuple.
    This function parses the parameter into the component colors

    :param colors: Either a tuple or a string that has both the text and background colors
    :type colors:  (str) or (str, str)
    :return:       tuple with the individual text and background colors
    :rtype:        (str, str)
    """
    if colors is None:
        return None, None
    dual_color = colors
    kw_text_color = kw_background_color = None
    try:
        if isinstance(dual_color, tuple):
            kw_text_color = dual_color[0]
            kw_background_color = dual_color[1]
        elif isinstance(dual_color, str):
            if ' on ' in dual_color:  # if has "on" in the string, then have both text and background
                kw_text_color = dual_color.split(' on ')[0]
                kw_background_color = dual_color.split(' on ')[1]
            else:  # if no "on" then assume the color string is just the text color
                kw_text_color = dual_color
    except Exception as e:
        print('* warning * you messed up with color formatting', e)

    return kw_text_color, kw_background_color


# ============================== set_global_icon ====#
# Sets the icon to be used by default                #
# ===================================================#
def set_global_icon(icon):
    """
    Sets the icon which will be used any time a window is created if an icon is not provided when the
    window is created.

    :param icon: Either a Base64 byte string or a filename
    :type icon:  bytes | str
    """

    Window._user_defined_icon = icon


# ============================== set_options ========#
# Sets the icon to be used by default                #
# ===================================================#
def set_options(
    icon=None,
    button_color=None,
    element_size=(None, None),
    button_element_size=(None, None),
    margins=(None, None),
    element_padding=(None, None),
    auto_size_text=None,
    auto_size_buttons=None,
    font=None,
    border_width=None,
    slider_border_width=None,
    slider_relief=None,
    slider_orientation=None,
    autoclose_time=None,
    message_box_line_width=None,
    progress_meter_border_depth=None,
    progress_meter_style=None,
    progress_meter_relief=None,
    progress_meter_color=None,
    progress_meter_size=None,
    text_justification=None,
    background_color=None,
    element_background_color=None,
    text_element_background_color=None,
    input_elements_background_color=None,
    input_text_color=None,
    scrollbar_color=None,
    text_color=None,
    element_text_color=None,
    debug_win_size=(None, None),
    window_location=(None, None),
    error_button_color=(None, None),
    tooltip_time=None,
    tooltip_font=None,
    use_ttk_buttons=None,
    ttk_theme=None,
    suppress_error_popups=None,
    suppress_raise_key_errors=None,
    suppress_key_guessing=None,
    warn_button_key_duplicates=False,
    enable_treeview_869_patch=None,
    enable_mac_notitlebar_patch=None,
    use_custom_titlebar=None,
    titlebar_background_color=None,
    titlebar_text_color=None,
    titlebar_font=None,
    titlebar_icon=None,
    user_settings_path=None,
    pysimplegui_settings_path=None,
    pysimplegui_settings_filename=None,
    keep_on_top=None,
    dpi_awareness=None,
    scaling=None,
    disable_modal_windows=None,
    force_modal_windows=None,
    tooltip_offset=(None, None),
    sbar_trough_color=None,
    sbar_background_color=None,
    sbar_arrow_color=None,
    sbar_width=None,
    sbar_arrow_width=None,
    sbar_frame_color=None,
    sbar_relief=None,
    alpha_channel=None,
    hide_window_when_creating=None,
    use_button_shortcuts=None,
    watermark_text=None,
):
    """
    :param icon:                            Can be either a filename or Base64 value. For Windows if filename, it MUST be ICO format. For Linux, must NOT be ICO. Most portable is to use a Base64 of a PNG file. This works universally across all OS's
    :type icon:                             bytes | str
    :param button_color:                    Color of the button (text, background)
    :type button_color:                     (str, str) | str
    :param element_size:                    element size (width, height) in characters
    :type element_size:                     (int, int)
    :param button_element_size:             Size of button
    :type button_element_size:              (int, int)
    :param margins:                         (left/right, top/bottom) tkinter margins around outsize. Amount of pixels to leave inside the window's frame around the edges before your elements are shown.
    :type margins:                          (int, int)
    :param element_padding:                 Default amount of padding to put around elements in window (left/right, top/bottom) or ((left, right), (top, bottom))
    :type element_padding:                  (int, int) or ((int, int),(int,int))
    :param auto_size_text:                  True if the Widget should be shrunk to exactly fit the number of chars to show
    :type auto_size_text:                   bool
    :param auto_size_buttons:               True if Buttons in this Window should be sized to exactly fit the text on this.
    :type auto_size_buttons:                (bool)
    :param font:                            specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                             (str or (str, int[, str]) or None)
    :param border_width:                    width of border around element
    :type border_width:                     (int)
    :param slider_border_width:             Width of the border around sliders
    :type slider_border_width:              (int)
    :param slider_relief:                   Type of relief to use for sliders
    :type slider_relief:                    (str)
    :param slider_orientation:              ???
    :type slider_orientation:               ???
    :param autoclose_time:                  ???
    :type autoclose_time:                   ???
    :param message_box_line_width:          ???
    :type message_box_line_width:           ???
    :param progress_meter_border_depth:     ???
    :type progress_meter_border_depth:      ???
    :param progress_meter_style:            You can no longer set a progress bar style. All ttk styles must be the same for the window
    :type progress_meter_style:             ???
    :param progress_meter_relief:
    :type progress_meter_relief:            ???
    :param progress_meter_color:            ???
    :type progress_meter_color:             ???
    :param progress_meter_size:             ???
    :type progress_meter_size:              ???
    :param text_justification:              Default text justification for all Text Elements in window
    :type text_justification:               'left' | 'right' | 'center'
    :param background_color:                color of background
    :type background_color:                 (str)
    :param element_background_color:        element background color
    :type element_background_color:         (str)
    :param text_element_background_color:   text element background color
    :type text_element_background_color:    (str)
    :param input_elements_background_color: Default color to use for the background of input elements
    :type input_elements_background_color:  (str)
    :param input_text_color:                Default color to use for the text for Input elements
    :type input_text_color:                 (str)
    :param scrollbar_color:                 Default color to use for the slider trough
    :type scrollbar_color:                  (str)
    :param text_color:                      color of the text
    :type text_color:                       (str)
    :param element_text_color:              Default color to use for Text elements
    :type element_text_color:               (str)
    :param debug_win_size:                  window size
    :type debug_win_size:                   (int, int)
    :param window_location:                 Default location to place windows. Not setting will center windows on the display
    :type window_location:                  (int, int) | None
    :param error_button_color:              (Default = (None))
    :type error_button_color:               ???
    :param tooltip_time:                    time in milliseconds to wait before showing a tooltip. Default is 400ms
    :type tooltip_time:                     (int)
    :param tooltip_font:                    font to use for all tooltips
    :type tooltip_font:                     str or Tuple[str, int] or Tuple[str, int, str]
    :param use_ttk_buttons:                 if True will cause all buttons to be ttk buttons
    :type use_ttk_buttons:                  (bool)
    :param ttk_theme:                       Theme to use with ttk widgets.  Choices (on Windows) include - 'default', 'winnative', 'clam', 'alt', 'classic', 'vista', 'xpnative'
    :type ttk_theme:                        (str)
    :param suppress_error_popups:           If True then error popups will not be shown if generated internally to PySimpleGUI
    :type suppress_error_popups:            (bool)
    :param suppress_raise_key_errors:       If True then key errors won't be raised (you'll still get popup error)
    :type suppress_raise_key_errors:        (bool)
    :param suppress_key_guessing:           If True then key errors won't try and find closest matches for you
    :type suppress_key_guessing:            (bool)
    :param warn_button_key_duplicates:      If True then duplicate Button Keys generate warnings (not recommended as they're expected)
    :type warn_button_key_duplicates:       (bool)
    :param enable_treeview_869_patch:       If True, then will use the treeview color patch for tk 8.6.9
    :type enable_treeview_869_patch:        (bool)
    :param enable_mac_notitlebar_patch:     If True then Windows with no titlebar use an alternative technique when tkinter version < 8.6.10
    :type enable_mac_notitlebar_patch:      (bool)
    :param use_custom_titlebar:             If True then a custom titlebar is used instead of the normal system titlebar
    :type use_custom_titlebar:              (bool)
    :param titlebar_background_color:       If custom titlebar indicated by use_custom_titlebar, then use this as background color
    :type titlebar_background_color:        str | None
    :param titlebar_text_color:             If custom titlebar indicated by use_custom_titlebar, then use this as text color
    :type titlebar_text_color:              str | None
    :param titlebar_font:                   If custom titlebar indicated by use_custom_titlebar, then use this as title font
    :type titlebar_font:                    (str or (str, int[, str]) or None) | None
    :param titlebar_icon:                   If custom titlebar indicated by use_custom_titlebar, then use this as the icon (file or base64 bytes)
    :type titlebar_icon:                    bytes | str
    :param user_settings_path:              default path for user_settings API calls. Expanded with os.path.expanduser so can contain ~ to represent user
    :type user_settings_path:               (str)
    :param pysimplegui_settings_path:       default path for the global PySimpleGUI user_settings
    :type pysimplegui_settings_path:        (str)
    :param pysimplegui_settings_filename:   default filename for the global PySimpleGUI user_settings
    :type pysimplegui_settings_filename:    (str)
    :param keep_on_top:                     If True then all windows will automatically be set to keep_on_top=True
    :type keep_on_top:                      (bool)
    :param dpi_awareness:                   If True then will turn on DPI awareness (Windows only at the moment)
    :type dpi_awareness:                    (bool)
    :param scaling:                         Sets the default scaling for all windows including popups, etc.
    :type scaling:                          (float)
    :param disable_modal_windows:           If True then all windows, including popups, will not be modal windows (unless they've been set to FORCED using another option)
    :type disable_modal_windows:            (bool)
    :param force_modal_windows:             If True then all windows will be modal (the disable option will be ignored... all windows will be forced to be modal)
    :type force_modal_windows:              (bool)
    :param tooltip_offset:                  Offset to use for tooltips as a tuple. These values will be added to the mouse location when the widget was entered.
    :type tooltip_offset:                   ((None, None) | (int, int))
    :param sbar_trough_color:               Scrollbar color of the trough
    :type sbar_trough_color:                (str)
    :param sbar_background_color:           Scrollbar color of the background of the arrow buttons at the ends AND the color of the "thumb" (the thing you grab and slide). Switches to arrow color when mouse is over
    :type sbar_background_color:            (str)
    :param sbar_arrow_color:                Scrollbar color of the arrow at the ends of the scrollbar (it looks like a button). Switches to background color when mouse is over
    :type sbar_arrow_color:                 (str)
    :param sbar_width:                      Scrollbar width in pixels
    :type sbar_width:                       (int)
    :param sbar_arrow_width:                Scrollbar width of the arrow on the scrollbar. It will potentially impact the overall width of the scrollbar
    :type sbar_arrow_width:                 (int)
    :param sbar_frame_color:                Scrollbar Color of frame around scrollbar (available only on some ttk themes)
    :type sbar_frame_color:                 (str)
    :param sbar_relief:                     Scrollbar relief that will be used for the "thumb" of the scrollbar (the thing you grab that slides). Should be a constant that is defined at starting with "RELIEF_" - RELIEF_RAISED, RELIEF_SUNKEN, RELIEF_FLAT, RELIEF_RIDGE, RELIEF_GROOVE, RELIEF_SOLID
    :type sbar_relief:                      (str)
    :param alpha_channel:                   Default alpha channel to be used on all windows
    :type alpha_channel:                    (float)
    :param hide_window_when_creating:       If True then alpha will be set to 0 while a window is made and moved to location indicated
    :type hide_window_when_creating:        (bool)
    :param use_button_shortcuts:            If True then Shortcut Char will be used with Buttons
    :type use_button_shortcuts:             (bool)
    :param watermark_text:                  Set the text that will be used if a window is watermarked
    :type watermark_text:                   (str)
    :return:                                None
    :rtype:                                 None
    """

    global DEFAULT_ELEMENT_SIZE
    global DEFAULT_BUTTON_ELEMENT_SIZE
    global DEFAULT_MARGINS  # Margins for each LEFT/RIGHT margin is first term
    global DEFAULT_ELEMENT_PADDING  # Padding between elements (row, col) in pixels
    global DEFAULT_AUTOSIZE_TEXT
    global DEFAULT_AUTOSIZE_BUTTONS
    global DEFAULT_FONT
    global DEFAULT_BORDER_WIDTH
    global DEFAULT_AUTOCLOSE_TIME
    global DEFAULT_BUTTON_COLOR
    global MESSAGE_BOX_LINE_WIDTH
    global DEFAULT_PROGRESS_BAR_BORDER_WIDTH
    global DEFAULT_PROGRESS_BAR_STYLE
    global DEFAULT_PROGRESS_BAR_RELIEF
    global DEFAULT_PROGRESS_BAR_COLOR
    global DEFAULT_PROGRESS_BAR_SIZE
    global DEFAULT_TEXT_JUSTIFICATION
    global DEFAULT_DEBUG_WINDOW_SIZE
    global DEFAULT_SLIDER_BORDER_WIDTH
    global DEFAULT_SLIDER_RELIEF
    global DEFAULT_SLIDER_ORIENTATION
    global DEFAULT_BACKGROUND_COLOR
    global DEFAULT_INPUT_ELEMENTS_COLOR
    global DEFAULT_ELEMENT_BACKGROUND_COLOR
    global DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR
    global DEFAULT_SCROLLBAR_COLOR
    global DEFAULT_TEXT_COLOR
    global DEFAULT_WINDOW_LOCATION
    global DEFAULT_ELEMENT_TEXT_COLOR
    global DEFAULT_INPUT_TEXT_COLOR
    global DEFAULT_TOOLTIP_TIME
    global DEFAULT_ERROR_BUTTON_COLOR
    global DEFAULT_TTK_THEME
    global USE_TTK_BUTTONS
    global TOOLTIP_FONT
    global SUPPRESS_ERROR_POPUPS
    global SUPPRESS_RAISE_KEY_ERRORS
    global SUPPRESS_KEY_GUESSING
    global WARN_DUPLICATE_BUTTON_KEY_ERRORS
    global ENABLE_TREEVIEW_869_PATCH
    global ENABLE_MAC_NOTITLEBAR_PATCH
    global USE_CUSTOM_TITLEBAR
    global CUSTOM_TITLEBAR_BACKGROUND_COLOR
    global CUSTOM_TITLEBAR_TEXT_COLOR
    global CUSTOM_TITLEBAR_ICON
    global CUSTOM_TITLEBAR_FONT
    global DEFAULT_USER_SETTINGS_PATH
    global DEFAULT_USER_SETTINGS_PYSIMPLEGUI_PATH
    global DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME
    global DEFAULT_KEEP_ON_TOP
    global DEFAULT_SCALING
    global DEFAULT_MODAL_WINDOWS_ENABLED
    global DEFAULT_MODAL_WINDOWS_FORCED
    global DEFAULT_TOOLTIP_OFFSET
    global DEFAULT_ALPHA_CHANNEL
    global _pysimplegui_user_settings
    global ttk_part_overrides_from_options
    global DEFAULT_HIDE_WINDOW_WHEN_CREATING
    global DEFAULT_USE_BUTTON_SHORTCUTS
    # global _my_windows

    if icon:
        Window._user_defined_icon = icon
        # _my_windows._user_defined_icon = icon

    if button_color is not None:
        if button_color == COLOR_SYSTEM_DEFAULT:
            DEFAULT_BUTTON_COLOR = (COLOR_SYSTEM_DEFAULT, COLOR_SYSTEM_DEFAULT)
        else:
            DEFAULT_BUTTON_COLOR = button_color

    if element_size != (None, None):
        DEFAULT_ELEMENT_SIZE = element_size

    if button_element_size != (None, None):
        DEFAULT_BUTTON_ELEMENT_SIZE = button_element_size

    if margins != (None, None):
        DEFAULT_MARGINS = margins

    if element_padding != (None, None):
        DEFAULT_ELEMENT_PADDING = element_padding

    if auto_size_text is not None:
        DEFAULT_AUTOSIZE_TEXT = auto_size_text

    if auto_size_buttons is not None:
        DEFAULT_AUTOSIZE_BUTTONS = auto_size_buttons

    if font is not None:
        DEFAULT_FONT = font

    if border_width is not None:
        DEFAULT_BORDER_WIDTH = border_width

    if autoclose_time is not None:
        DEFAULT_AUTOCLOSE_TIME = autoclose_time

    if message_box_line_width is not None:
        MESSAGE_BOX_LINE_WIDTH = message_box_line_width

    if progress_meter_border_depth is not None:
        DEFAULT_PROGRESS_BAR_BORDER_WIDTH = progress_meter_border_depth

    if progress_meter_style is not None:
        warnings.warn('You can no longer set a progress bar style. All ttk styles must be the same for the window', UserWarning)
        # DEFAULT_PROGRESS_BAR_STYLE = progress_meter_style

    if progress_meter_relief is not None:
        DEFAULT_PROGRESS_BAR_RELIEF = progress_meter_relief

    if progress_meter_color is not None:
        DEFAULT_PROGRESS_BAR_COLOR = progress_meter_color

    if progress_meter_size is not None:
        DEFAULT_PROGRESS_BAR_SIZE = progress_meter_size

    if slider_border_width is not None:
        DEFAULT_SLIDER_BORDER_WIDTH = slider_border_width

    if slider_orientation is not None:
        DEFAULT_SLIDER_ORIENTATION = slider_orientation

    if slider_relief is not None:
        DEFAULT_SLIDER_RELIEF = slider_relief

    if text_justification is not None:
        DEFAULT_TEXT_JUSTIFICATION = text_justification

    if background_color is not None:
        DEFAULT_BACKGROUND_COLOR = background_color

    if text_element_background_color is not None:
        DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR = text_element_background_color

    if input_elements_background_color is not None:
        DEFAULT_INPUT_ELEMENTS_COLOR = input_elements_background_color

    if element_background_color is not None:
        DEFAULT_ELEMENT_BACKGROUND_COLOR = element_background_color

    if window_location != (None, None):
        DEFAULT_WINDOW_LOCATION = window_location

    if debug_win_size != (None, None):
        DEFAULT_DEBUG_WINDOW_SIZE = debug_win_size

    if text_color is not None:
        DEFAULT_TEXT_COLOR = text_color

    if scrollbar_color is not None:
        DEFAULT_SCROLLBAR_COLOR = scrollbar_color

    if element_text_color is not None:
        DEFAULT_ELEMENT_TEXT_COLOR = element_text_color

    if input_text_color is not None:
        DEFAULT_INPUT_TEXT_COLOR = input_text_color

    if tooltip_time is not None:
        DEFAULT_TOOLTIP_TIME = tooltip_time

    if error_button_color != (None, None):
        DEFAULT_ERROR_BUTTON_COLOR = error_button_color

    if ttk_theme is not None:
        DEFAULT_TTK_THEME = ttk_theme

    if use_ttk_buttons is not None:
        USE_TTK_BUTTONS = use_ttk_buttons

    if tooltip_font is not None:
        TOOLTIP_FONT = tooltip_font

    if suppress_error_popups is not None:
        SUPPRESS_ERROR_POPUPS = suppress_error_popups

    if suppress_raise_key_errors is not None:
        SUPPRESS_RAISE_KEY_ERRORS = suppress_raise_key_errors

    if suppress_key_guessing is not None:
        SUPPRESS_KEY_GUESSING = suppress_key_guessing

    if warn_button_key_duplicates is not None:
        WARN_DUPLICATE_BUTTON_KEY_ERRORS = warn_button_key_duplicates

    if enable_treeview_869_patch is not None:
        ENABLE_TREEVIEW_869_PATCH = enable_treeview_869_patch

    if enable_mac_notitlebar_patch is not None:
        ENABLE_MAC_NOTITLEBAR_PATCH = enable_mac_notitlebar_patch

    if use_custom_titlebar is not None:
        USE_CUSTOM_TITLEBAR = use_custom_titlebar

    if titlebar_background_color is not None:
        CUSTOM_TITLEBAR_BACKGROUND_COLOR = titlebar_background_color

    if titlebar_text_color is not None:
        CUSTOM_TITLEBAR_TEXT_COLOR = titlebar_text_color

    if titlebar_font is not None:
        CUSTOM_TITLEBAR_FONT = titlebar_font

    if titlebar_icon is not None:
        CUSTOM_TITLEBAR_ICON = titlebar_icon

    if user_settings_path is not None:
        DEFAULT_USER_SETTINGS_PATH = user_settings_path

    if pysimplegui_settings_path is not None:
        DEFAULT_USER_SETTINGS_PYSIMPLEGUI_PATH = pysimplegui_settings_path

    if pysimplegui_settings_filename is not None:
        DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME = pysimplegui_settings_filename

    if pysimplegui_settings_filename is not None or pysimplegui_settings_filename is not None:
        _pysimplegui_user_settings = UserSettings(filename=DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME, path=DEFAULT_USER_SETTINGS_PYSIMPLEGUI_PATH)

    if keep_on_top is not None:
        DEFAULT_KEEP_ON_TOP = keep_on_top

    if dpi_awareness is True:
        if running_windows():
            if platform.release() == '7':
                ctypes.windll.user32.SetProcessDPIAware()
            elif platform.release() == '8' or platform.release() == '10':
                ctypes.windll.shcore.SetProcessDpiAwareness(1)

    if scaling is not None:
        DEFAULT_SCALING = scaling

    if disable_modal_windows is not None:
        DEFAULT_MODAL_WINDOWS_ENABLED = not disable_modal_windows

    if force_modal_windows is not None:
        DEFAULT_MODAL_WINDOWS_FORCED = force_modal_windows

    if tooltip_offset != (None, None):
        DEFAULT_TOOLTIP_OFFSET = tooltip_offset

    if alpha_channel is not None:
        DEFAULT_ALPHA_CHANNEL = alpha_channel

    # ---------------- ttk scrollbar section ----------------
    if sbar_background_color is not None:
        ttk_part_overrides_from_options.sbar_background_color = sbar_background_color

    if sbar_trough_color is not None:
        ttk_part_overrides_from_options.sbar_trough_color = sbar_trough_color

    if sbar_arrow_color is not None:
        ttk_part_overrides_from_options.sbar_arrow_color = sbar_arrow_color

    if sbar_frame_color is not None:
        ttk_part_overrides_from_options.sbar_frame_color = sbar_frame_color

    if sbar_relief is not None:
        ttk_part_overrides_from_options.sbar_relief = sbar_relief

    if sbar_arrow_width is not None:
        ttk_part_overrides_from_options.sbar_arrow_width = sbar_arrow_width

    if sbar_width is not None:
        ttk_part_overrides_from_options.sbar_width = sbar_width

    if hide_window_when_creating is not None:
        DEFAULT_HIDE_WINDOW_WHEN_CREATING = hide_window_when_creating

    if use_button_shortcuts is not None:
        DEFAULT_USE_BUTTON_SHORTCUTS = use_button_shortcuts

    if watermark_text is not None:
        Window._watermark_user_text = watermark_text

    return True


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

# .########.##.....##.########.##.....##.########..######.
# ....##....##.....##.##.......###...###.##.......##....##
# ....##....##.....##.##.......####.####.##.......##......
# ....##....#########.######...##.###.##.######....######.
# ....##....##.....##.##.......##.....##.##.............##
# ....##....##.....##.##.......##.....##.##.......##....##
# ....##....##.....##.########.##.....##.########..######.

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

# The official Theme code

#################### ChangeLookAndFeel #######################
# Predefined settings that will change the colors and styles #
# of the elements.                                           #
##############################################################
# fmt: off
LOOK_AND_FEEL_TABLE = {
    'SystemDefault': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'SystemDefaultForReal': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': COLOR_SYSTEM_DEFAULT,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'SystemDefault1': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': COLOR_SYSTEM_DEFAULT,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'Material1': {'BACKGROUND': '#E3F2FD','TEXT': '#000000','INPUT': '#86A8FF','TEXT_INPUT': '#000000','SCROLL': '#86A8FF','BUTTON': ('#FFFFFF', '#5079D3'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 0,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#FF0266','ACCENT2': '#FF5C93','ACCENT3': '#C5003C',},
    'Material2': {'BACKGROUND': '#FAFAFA','TEXT': '#000000','INPUT': '#004EA1','TEXT_INPUT': '#FFFFFF','SCROLL': '#5EA7FF','BUTTON': ('#FFFFFF', '#0079D3'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 0,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#FF0266','ACCENT2': '#FF5C93','ACCENT3': '#C5003C',},
    'Reddit': {'BACKGROUND': '#ffffff','TEXT': '#1a1a1b','INPUT': '#dae0e6','TEXT_INPUT': '#222222','SCROLL': '#a5a4a4','BUTTON': ('#FFFFFF', '#0079d3'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#ff5414','ACCENT2': '#33a8ff','ACCENT3': '#dbf0ff',},
    'Topanga': {'BACKGROUND': '#282923','TEXT': '#E7DB74','INPUT': '#393a32','TEXT_INPUT': '#E7C855','SCROLL': '#E7C855','BUTTON': ('#E7C855', '#284B5A'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#c15226','ACCENT2': '#7a4d5f','ACCENT3': '#889743',},
    'GreenTan': {'BACKGROUND': '#9FB8AD','TEXT': '#000000','INPUT': '#F7F3EC','TEXT_INPUT': '#000000','SCROLL': '#F7F3EC','BUTTON': ('#FFFFFF', '#475841'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Dark': {'BACKGROUND': '#404040','TEXT': '#FFFFFF','INPUT': '#4D4D4D','TEXT_INPUT': '#FFFFFF','SCROLL': '#707070','BUTTON': ('#FFFFFF', '#004F00'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightGreen': {'BACKGROUND': '#B7CECE','TEXT': '#000000','INPUT': '#FDFFF7','TEXT_INPUT': '#000000','SCROLL': '#FDFFF7','BUTTON': ('#FFFFFF', '#658268'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'ACCENT1': '#76506d','ACCENT2': '#5148f1','ACCENT3': '#0a1c84','PROGRESS_DEPTH': 0,},
    'Dark2': {'BACKGROUND': '#404040','TEXT': '#FFFFFF','INPUT': '#FFFFFF','TEXT_INPUT': '#000000','SCROLL': '#707070','BUTTON': ('#FFFFFF', '#004F00'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Black': {'BACKGROUND': '#000000','TEXT': '#FFFFFF','INPUT': '#4D4D4D','TEXT_INPUT': '#FFFFFF','SCROLL': '#707070','BUTTON': ('#000000', '#FFFFFF'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Black2': {'BACKGROUND': '#000000','TEXT': '#FFFFFF','INPUT': '#000000','TEXT_INPUT': '#FFFFFF','SCROLL': '#FFFFFF','BUTTON': ('#000000', '#FFFFFF'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Tan': {'BACKGROUND': '#fdf6e3','TEXT': '#268bd1','INPUT': '#eee8d5','TEXT_INPUT': '#6c71c3','SCROLL': '#eee8d5','BUTTON': ('#FFFFFF', '#063542'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'TanBlue': {'BACKGROUND': '#e5dece','TEXT': '#063289','INPUT': '#f9f8f4','TEXT_INPUT': '#242834','SCROLL': '#eee8d5','BUTTON': ('#FFFFFF', '#063289'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkTanBlue': {'BACKGROUND': '#242834','TEXT': '#dfe6f8','INPUT': '#97755c','TEXT_INPUT': '#FFFFFF','SCROLL': '#a9afbb','BUTTON': ('#FFFFFF', '#063289'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkAmber': {'BACKGROUND': '#2c2825','TEXT': '#fdcb52','INPUT': '#705e52','TEXT_INPUT': '#fdcb52','SCROLL': '#705e52','BUTTON': ('#000000', '#fdcb52'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBlue': {'BACKGROUND': '#1a2835','TEXT': '#d1ecff','INPUT': '#335267','TEXT_INPUT': '#acc2d0','SCROLL': '#1b6497','BUTTON': ('#000000', '#fafaf8'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Reds': {'BACKGROUND': '#280001','TEXT': '#FFFFFF','INPUT': '#d8d584','TEXT_INPUT': '#000000','SCROLL': '#763e00','BUTTON': ('#000000', '#daad28'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Green': {'BACKGROUND': '#82a459','TEXT': '#000000','INPUT': '#d8d584','TEXT_INPUT': '#000000','SCROLL': '#e3ecf3','BUTTON': ('#FFFFFF', '#517239'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'BluePurple': {'BACKGROUND': '#A5CADD','TEXT': '#6E266E','INPUT': '#E0F5FF','TEXT_INPUT': '#000000','SCROLL': '#E0F5FF','BUTTON': ('#FFFFFF', '#303952'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Purple': {'BACKGROUND': '#B0AAC2','TEXT': '#000000','INPUT': '#F2EFE8','SCROLL': '#F2EFE8','TEXT_INPUT': '#000000','BUTTON': ('#000000', '#C2D4D8'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'BlueMono': {'BACKGROUND': '#AAB6D3','TEXT': '#000000','INPUT': '#F1F4FC','SCROLL': '#F1F4FC','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#7186C7'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'GreenMono': {'BACKGROUND': '#A8C1B4','TEXT': '#000000','INPUT': '#DDE0DE','SCROLL': '#E3E3E3','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#6D9F85'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'BrownBlue': {'BACKGROUND': '#64778d','TEXT': '#FFFFFF','INPUT': '#f0f3f7','SCROLL': '#A6B2BE','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#283b5b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'BrightColors': {'BACKGROUND': '#b4ffb4','TEXT': '#000000','INPUT': '#ffff64','SCROLL': '#ffb482','TEXT_INPUT': '#000000','BUTTON': ('#000000', '#ffa0dc'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'NeutralBlue': {'BACKGROUND': '#92aa9d','TEXT': '#000000','INPUT': '#fcfff6','SCROLL': '#fcfff6','TEXT_INPUT': '#000000','BUTTON': ('#000000', '#d0dbbd'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Kayak': {'BACKGROUND': '#a7ad7f','TEXT': '#000000','INPUT': '#e6d3a8','SCROLL': '#e6d3a8','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#5d907d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'SandyBeach': {'BACKGROUND': '#efeccb','TEXT': '#012f2f','INPUT': '#e6d3a8','SCROLL': '#e6d3a8','TEXT_INPUT': '#012f2f','BUTTON': ('#FFFFFF', '#046380'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'TealMono': {'BACKGROUND': '#a8cfdd','TEXT': '#000000','INPUT': '#dfedf2','SCROLL': '#dfedf2','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#183440'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'Default': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'Default1': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': COLOR_SYSTEM_DEFAULT,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'DefaultNoMoreNagging': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'GrayGrayGray': {'BACKGROUND': COLOR_SYSTEM_DEFAULT,'TEXT': COLOR_SYSTEM_DEFAULT,'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT': COLOR_SYSTEM_DEFAULT,'SCROLL': COLOR_SYSTEM_DEFAULT,'BUTTON': COLOR_SYSTEM_DEFAULT,'PROGRESS': COLOR_SYSTEM_DEFAULT,'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'LightBlue': {'BACKGROUND': '#E3F2FD','TEXT': '#000000','INPUT': '#86A8FF','TEXT_INPUT': '#000000','SCROLL': '#86A8FF','BUTTON': ('#FFFFFF', '#5079D3'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 0,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#FF0266','ACCENT2': '#FF5C93','ACCENT3': '#C5003C',},
    'LightGrey': {'BACKGROUND': '#FAFAFA','TEXT': '#000000','INPUT': '#004EA1','TEXT_INPUT': '#FFFFFF','SCROLL': '#5EA7FF','BUTTON': ('#FFFFFF', '#0079D3'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 0,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#FF0266','ACCENT2': '#FF5C93','ACCENT3': '#C5003C',},
    'LightGrey1': {'BACKGROUND': '#ffffff','TEXT': '#1a1a1b','INPUT': '#dae0e6','TEXT_INPUT': '#222222','SCROLL': '#a5a4a4','BUTTON': ('#FFFFFF', '#0079d3'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#ff5414','ACCENT2': '#33a8ff','ACCENT3': '#dbf0ff',},
    'DarkBrown': {'BACKGROUND': '#282923','TEXT': '#E7DB74','INPUT': '#393a32','TEXT_INPUT': '#E7C855','SCROLL': '#E7C855','BUTTON': ('#E7C855', '#284B5A'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'ACCENT1': '#c15226','ACCENT2': '#7a4d5f','ACCENT3': '#889743',},
    'LightGreen1': {'BACKGROUND': '#9FB8AD','TEXT': '#000000','INPUT': '#F7F3EC','TEXT_INPUT': '#000000','SCROLL': '#F7F3EC','BUTTON': ('#FFFFFF', '#475841'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey': {'BACKGROUND': '#404040','TEXT': '#FFFFFF','INPUT': '#4D4D4D','TEXT_INPUT': '#FFFFFF','SCROLL': '#707070','BUTTON': ('#FFFFFF', '#004F00'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightGreen2': {'BACKGROUND': '#B7CECE','TEXT': '#000000','INPUT': '#FDFFF7','TEXT_INPUT': '#000000','SCROLL': '#FDFFF7','BUTTON': ('#FFFFFF', '#658268'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'ACCENT1': '#76506d','ACCENT2': '#5148f1','ACCENT3': '#0a1c84','PROGRESS_DEPTH': 0,},
    'DarkGrey1': {'BACKGROUND': '#404040','TEXT': '#FFFFFF','INPUT': '#FFFFFF','TEXT_INPUT': '#000000','SCROLL': '#707070','BUTTON': ('#FFFFFF', '#004F00'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBlack': {'BACKGROUND': '#000000','TEXT': '#FFFFFF','INPUT': '#4D4D4D','TEXT_INPUT': '#FFFFFF','SCROLL': '#707070','BUTTON': ('#000000', '#FFFFFF'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBrown': {'BACKGROUND': '#fdf6e3','TEXT': '#268bd1','INPUT': '#eee8d5','TEXT_INPUT': '#6c71c3','SCROLL': '#eee8d5','BUTTON': ('#FFFFFF', '#063542'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBrown1': {'BACKGROUND': '#e5dece','TEXT': '#063289','INPUT': '#f9f8f4','TEXT_INPUT': '#242834','SCROLL': '#eee8d5','BUTTON': ('#FFFFFF', '#063289'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBlue1': {'BACKGROUND': '#242834','TEXT': '#dfe6f8','INPUT': '#97755c','TEXT_INPUT': '#FFFFFF','SCROLL': '#a9afbb','BUTTON': ('#FFFFFF', '#063289'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBrown1': {'BACKGROUND': '#2c2825','TEXT': '#fdcb52','INPUT': '#705e52','TEXT_INPUT': '#fdcb52','SCROLL': '#705e52','BUTTON': ('#000000', '#fdcb52'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBlue2': {'BACKGROUND': '#1a2835','TEXT': '#d1ecff','INPUT': '#335267','TEXT_INPUT': '#acc2d0','SCROLL': '#1b6497','BUTTON': ('#000000', '#fafaf8'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBrown2': {'BACKGROUND': '#280001','TEXT': '#FFFFFF','INPUT': '#d8d584','TEXT_INPUT': '#000000','SCROLL': '#763e00','BUTTON': ('#000000', '#daad28'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGreen': {'BACKGROUND': '#82a459','TEXT': '#000000','INPUT': '#d8d584','TEXT_INPUT': '#000000','SCROLL': '#e3ecf3','BUTTON': ('#FFFFFF', '#517239'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBlue1': {'BACKGROUND': '#A5CADD','TEXT': '#6E266E','INPUT': '#E0F5FF','TEXT_INPUT': '#000000','SCROLL': '#E0F5FF','BUTTON': ('#FFFFFF', '#303952'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightPurple': {'BACKGROUND': '#B0AAC2','TEXT': '#000000','INPUT': '#F2EFE8','SCROLL': '#F2EFE8','TEXT_INPUT': '#000000','BUTTON': ('#000000', '#C2D4D8'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBlue2': {'BACKGROUND': '#AAB6D3','TEXT': '#000000','INPUT': '#F1F4FC','SCROLL': '#F1F4FC','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#7186C7'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightGreen3': {'BACKGROUND': '#A8C1B4','TEXT': '#000000','INPUT': '#DDE0DE','SCROLL': '#E3E3E3','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#6D9F85'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBlue3': {'BACKGROUND': '#64778d','TEXT': '#FFFFFF','INPUT': '#f0f3f7','SCROLL': '#A6B2BE','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#283b5b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightGreen4': {'BACKGROUND': '#b4ffb4','TEXT': '#000000','INPUT': '#ffff64','SCROLL': '#ffb482','TEXT_INPUT': '#000000','BUTTON': ('#000000', '#ffa0dc'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightGreen5': {'BACKGROUND': '#92aa9d','TEXT': '#000000','INPUT': '#fcfff6','SCROLL': '#fcfff6','TEXT_INPUT': '#000000','BUTTON': ('#000000', '#d0dbbd'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBrown2': {'BACKGROUND': '#a7ad7f','TEXT': '#000000','INPUT': '#e6d3a8','SCROLL': '#e6d3a8','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#5d907d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBrown3': {'BACKGROUND': '#efeccb','TEXT': '#012f2f','INPUT': '#e6d3a8','SCROLL': '#e6d3a8','TEXT_INPUT': '#012f2f','BUTTON': ('#FFFFFF', '#046380'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBlue3': {'BACKGROUND': '#a8cfdd','TEXT': '#000000','INPUT': '#dfedf2','SCROLL': '#dfedf2','TEXT_INPUT': '#000000','BUTTON': ('#FFFFFF', '#183440'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'LightBrown4': {'BACKGROUND': '#d7c79e','TEXT': '#a35638','INPUT': '#9dab86','TEXT_INPUT': '#000000','SCROLL': '#a35638','BUTTON': ('#FFFFFF', '#a35638'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#a35638', '#9dab86', '#e08f62', '#d7c79e'],},
    'DarkTeal': {'BACKGROUND': '#003f5c','TEXT': '#fb5b5a','INPUT': '#bc4873','TEXT_INPUT': '#FFFFFF','SCROLL': '#bc4873','BUTTON': ('#FFFFFF', '#fb5b5a'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#003f5c', '#472b62', '#bc4873', '#fb5b5a'],},
    'DarkPurple': {'BACKGROUND': '#472b62','TEXT': '#fb5b5a','INPUT': '#bc4873','TEXT_INPUT': '#FFFFFF','SCROLL': '#bc4873','BUTTON': ('#FFFFFF', '#472b62'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#003f5c', '#472b62', '#bc4873', '#fb5b5a'],},
    'LightGreen6': {'BACKGROUND': '#eafbea','TEXT': '#1f6650','INPUT': '#6f9a8d','TEXT_INPUT': '#FFFFFF','SCROLL': '#1f6650','BUTTON': ('#FFFFFF', '#1f6650'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#1f6650', '#6f9a8d', '#ea5e5e', '#eafbea'],},
    'DarkGrey2': {'BACKGROUND': '#2b2b28','TEXT': '#f8f8f8','INPUT': '#f1d6ab','TEXT_INPUT': '#000000','SCROLL': '#f1d6ab','BUTTON': ('#2b2b28', '#e3b04b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#2b2b28', '#e3b04b', '#f1d6ab', '#f8f8f8'],},
    'LightBrown6': {'BACKGROUND': '#f9b282','TEXT': '#8f4426','INPUT': '#de6b35','TEXT_INPUT': '#FFFFFF','SCROLL': '#8f4426','BUTTON': ('#FFFFFF', '#8f4426'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#8f4426', '#de6b35', '#64ccda', '#f9b282'],},
    'DarkTeal1': {'BACKGROUND': '#396362','TEXT': '#ffe7d1','INPUT': '#f6c89f','TEXT_INPUT': '#000000','SCROLL': '#f6c89f','BUTTON': ('#ffe7d1', '#4b8e8d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#396362', '#4b8e8d', '#f6c89f', '#ffe7d1'],},
    'LightBrown7': {'BACKGROUND': '#f6c89f','TEXT': '#396362','INPUT': '#4b8e8d','TEXT_INPUT': '#FFFFFF','SCROLL': '#396362','BUTTON': ('#FFFFFF', '#396362'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#396362', '#4b8e8d', '#f6c89f', '#ffe7d1'],},
    'DarkPurple1': {'BACKGROUND': '#0c093c','TEXT': '#fad6d6','INPUT': '#eea5f6','TEXT_INPUT': '#000000','SCROLL': '#eea5f6','BUTTON': ('#FFFFFF', '#df42d1'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#0c093c', '#df42d1', '#eea5f6', '#fad6d6'],},
    'DarkGrey3': {'BACKGROUND': '#211717','TEXT': '#dfddc7','INPUT': '#f58b54','TEXT_INPUT': '#000000','SCROLL': '#f58b54','BUTTON': ('#dfddc7', '#a34a28'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#211717', '#a34a28', '#f58b54', '#dfddc7'],},
    'LightBrown8': {'BACKGROUND': '#dfddc7','TEXT': '#211717','INPUT': '#a34a28','TEXT_INPUT': '#dfddc7','SCROLL': '#211717','BUTTON': ('#dfddc7', '#a34a28'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#211717', '#a34a28', '#f58b54', '#dfddc7'],},
    'DarkBlue4': {'BACKGROUND': '#494ca2','TEXT': '#e3e7f1','INPUT': '#c6cbef','TEXT_INPUT': '#000000','SCROLL': '#c6cbef','BUTTON': ('#FFFFFF', '#8186d5'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#494ca2', '#8186d5', '#c6cbef', '#e3e7f1'],},
    'LightBlue4': {'BACKGROUND': '#5c94bd','TEXT': '#470938','INPUT': '#1a3e59','TEXT_INPUT': '#FFFFFF','SCROLL': '#470938','BUTTON': ('#FFFFFF', '#470938'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#470938', '#1a3e59', '#5c94bd', '#f2d6eb'],},
    'DarkTeal2': {'BACKGROUND': '#394a6d','TEXT': '#c0ffb3','INPUT': '#52de97','TEXT_INPUT': '#000000','SCROLL': '#52de97','BUTTON': ('#c0ffb3', '#394a6d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#394a6d', '#3c9d9b', '#52de97', '#c0ffb3'],},
    'DarkTeal3': {'BACKGROUND': '#3c9d9b','TEXT': '#c0ffb3','INPUT': '#52de97','TEXT_INPUT': '#000000','SCROLL': '#52de97','BUTTON': ('#c0ffb3', '#394a6d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#394a6d', '#3c9d9b', '#52de97', '#c0ffb3'],},
    'DarkPurple5': {'BACKGROUND': '#730068','TEXT': '#f6f078','INPUT': '#01d28e','TEXT_INPUT': '#000000','SCROLL': '#01d28e','BUTTON': ('#f6f078', '#730068'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#730068', '#434982', '#01d28e', '#f6f078'],},
    'DarkPurple2': {'BACKGROUND': '#202060','TEXT': '#b030b0','INPUT': '#602080','TEXT_INPUT': '#FFFFFF','SCROLL': '#602080','BUTTON': ('#FFFFFF', '#202040'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#202040', '#202060', '#602080', '#b030b0'],},
    'DarkBlue5': {'BACKGROUND': '#000272','TEXT': '#ff6363','INPUT': '#a32f80','TEXT_INPUT': '#FFFFFF','SCROLL': '#a32f80','BUTTON': ('#FFFFFF', '#341677'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#000272', '#341677', '#a32f80', '#ff6363'],},
    'LightGrey2': {'BACKGROUND': '#f6f6f6','TEXT': '#420000','INPUT': '#d4d7dd','TEXT_INPUT': '#420000','SCROLL': '#420000','BUTTON': ('#420000', '#d4d7dd'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#420000', '#d4d7dd', '#eae9e9', '#f6f6f6'],},
    'LightGrey3': {'BACKGROUND': '#eae9e9','TEXT': '#420000','INPUT': '#d4d7dd','TEXT_INPUT': '#420000','SCROLL': '#420000','BUTTON': ('#420000', '#d4d7dd'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#420000', '#d4d7dd', '#eae9e9', '#f6f6f6'],},
    'DarkBlue6': {'BACKGROUND': '#01024e','TEXT': '#ff6464','INPUT': '#8b4367','TEXT_INPUT': '#FFFFFF','SCROLL': '#8b4367','BUTTON': ('#FFFFFF', '#543864'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#01024e', '#543864', '#8b4367', '#ff6464'],},
    'DarkBlue7': {'BACKGROUND': '#241663','TEXT': '#eae7af','INPUT': '#a72693','TEXT_INPUT': '#eae7af','SCROLL': '#a72693','BUTTON': ('#eae7af', '#160f30'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#160f30', '#241663', '#a72693', '#eae7af'],},
    'LightBrown9': {'BACKGROUND': '#f6d365','TEXT': '#3a1f5d','INPUT': '#c83660','TEXT_INPUT': '#f6d365','SCROLL': '#3a1f5d','BUTTON': ('#f6d365', '#c83660'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3a1f5d', '#c83660', '#e15249', '#f6d365'],},
    'DarkPurple3': {'BACKGROUND': '#6e2142','TEXT': '#ffd692','INPUT': '#e16363','TEXT_INPUT': '#ffd692','SCROLL': '#e16363','BUTTON': ('#ffd692', '#943855'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#6e2142', '#943855', '#e16363', '#ffd692'],},
    'LightBrown10': {'BACKGROUND': '#ffd692','TEXT': '#6e2142','INPUT': '#943855','TEXT_INPUT': '#FFFFFF','SCROLL': '#6e2142','BUTTON': ('#FFFFFF', '#6e2142'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#6e2142', '#943855', '#e16363', '#ffd692'],},
    'DarkPurple4': {'BACKGROUND': '#200f21','TEXT': '#f638dc','INPUT': '#5a3d5c','TEXT_INPUT': '#FFFFFF','SCROLL': '#5a3d5c','BUTTON': ('#FFFFFF', '#382039'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#200f21', '#382039', '#5a3d5c', '#f638dc'],},
    'LightBlue5': {'BACKGROUND': '#b2fcff','TEXT': '#3e64ff','INPUT': '#5edfff','TEXT_INPUT': '#000000','SCROLL': '#3e64ff','BUTTON': ('#FFFFFF', '#3e64ff'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3e64ff', '#5edfff', '#b2fcff', '#ecfcff'],},
    'DarkTeal4': {'BACKGROUND': '#464159','TEXT': '#c7f0db','INPUT': '#8bbabb','TEXT_INPUT': '#000000','SCROLL': '#8bbabb','BUTTON': ('#FFFFFF', '#6c7b95'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#464159', '#6c7b95', '#8bbabb', '#c7f0db'],},
    'LightTeal': {'BACKGROUND': '#c7f0db','TEXT': '#464159','INPUT': '#6c7b95','TEXT_INPUT': '#FFFFFF','SCROLL': '#464159','BUTTON': ('#FFFFFF', '#464159'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#464159', '#6c7b95', '#8bbabb', '#c7f0db'],},
    'DarkTeal5': {'BACKGROUND': '#8bbabb','TEXT': '#464159','INPUT': '#6c7b95','TEXT_INPUT': '#FFFFFF','SCROLL': '#464159','BUTTON': ('#c7f0db', '#6c7b95'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#464159', '#6c7b95', '#8bbabb', '#c7f0db'],},
    'LightGrey4': {'BACKGROUND': '#faf5ef','TEXT': '#672f2f','INPUT': '#99b19c','TEXT_INPUT': '#672f2f','SCROLL': '#672f2f','BUTTON': ('#672f2f', '#99b19c'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#672f2f', '#99b19c', '#d7d1c9', '#faf5ef'],},
    'LightGreen7': {'BACKGROUND': '#99b19c','TEXT': '#faf5ef','INPUT': '#d7d1c9','TEXT_INPUT': '#000000','SCROLL': '#d7d1c9','BUTTON': ('#FFFFFF', '#99b19c'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#672f2f', '#99b19c', '#d7d1c9', '#faf5ef'],},
    'LightGrey5': {'BACKGROUND': '#d7d1c9','TEXT': '#672f2f','INPUT': '#99b19c','TEXT_INPUT': '#672f2f','SCROLL': '#672f2f','BUTTON': ('#FFFFFF', '#672f2f'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#672f2f', '#99b19c', '#d7d1c9', '#faf5ef'],},
    'DarkBrown3': {'BACKGROUND': '#a0855b','TEXT': '#f9f6f2','INPUT': '#f1d6ab','TEXT_INPUT': '#000000','SCROLL': '#f1d6ab','BUTTON': ('#FFFFFF', '#38470b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#38470b', '#a0855b', '#f1d6ab', '#f9f6f2'],},
    'LightBrown11': {'BACKGROUND': '#f1d6ab','TEXT': '#38470b','INPUT': '#a0855b','TEXT_INPUT': '#FFFFFF','SCROLL': '#38470b','BUTTON': ('#f9f6f2', '#a0855b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#38470b', '#a0855b', '#f1d6ab', '#f9f6f2'],},
    'DarkRed': {'BACKGROUND': '#83142c','TEXT': '#f9d276','INPUT': '#ad1d45','TEXT_INPUT': '#FFFFFF','SCROLL': '#ad1d45','BUTTON': ('#f9d276', '#ad1d45'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#44000d', '#83142c', '#ad1d45', '#f9d276'],},
    'DarkTeal6': {'BACKGROUND': '#204969','TEXT': '#fff7f7','INPUT': '#dadada','TEXT_INPUT': '#000000','SCROLL': '#dadada','BUTTON': ('#000000', '#fff7f7'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#204969', '#08ffc8', '#dadada', '#fff7f7'],},
    'DarkBrown4': {'BACKGROUND': '#252525','TEXT': '#ff0000','INPUT': '#af0404','TEXT_INPUT': '#FFFFFF','SCROLL': '#af0404','BUTTON': ('#FFFFFF', '#252525'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#252525', '#414141', '#af0404', '#ff0000'],},
    'LightYellow': {'BACKGROUND': '#f4ff61','TEXT': '#27aa80','INPUT': '#32ff6a','TEXT_INPUT': '#000000','SCROLL': '#27aa80','BUTTON': ('#f4ff61', '#27aa80'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#27aa80', '#32ff6a', '#a8ff3e', '#f4ff61'],},
    'DarkGreen1': {'BACKGROUND': '#2b580c','TEXT': '#fdef96','INPUT': '#f7b71d','TEXT_INPUT': '#000000','SCROLL': '#f7b71d','BUTTON': ('#fdef96', '#2b580c'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#2b580c', '#afa939', '#f7b71d', '#fdef96'],},
    'LightGreen8': {'BACKGROUND': '#c8dad3','TEXT': '#63707e','INPUT': '#93b5b3','TEXT_INPUT': '#000000','SCROLL': '#63707e','BUTTON': ('#FFFFFF', '#63707e'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#63707e', '#93b5b3', '#c8dad3', '#f2f6f5'],},
    'DarkTeal7': {'BACKGROUND': '#248ea9','TEXT': '#fafdcb','INPUT': '#aee7e8','TEXT_INPUT': '#000000','SCROLL': '#aee7e8','BUTTON': ('#000000', '#fafdcb'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#248ea9', '#28c3d4', '#aee7e8', '#fafdcb'],},
    'DarkBlue8': {'BACKGROUND': '#454d66','TEXT': '#d9d872','INPUT': '#58b368','TEXT_INPUT': '#000000','SCROLL': '#58b368','BUTTON': ('#000000', '#009975'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#009975', '#454d66', '#58b368', '#d9d872'],},
    'DarkBlue9': {'BACKGROUND': '#263859','TEXT': '#ff6768','INPUT': '#6b778d','TEXT_INPUT': '#FFFFFF','SCROLL': '#6b778d','BUTTON': ('#ff6768', '#263859'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#17223b', '#263859', '#6b778d', '#ff6768'],},
    'DarkBlue10': {'BACKGROUND': '#0028ff','TEXT': '#f1f4df','INPUT': '#10eaf0','TEXT_INPUT': '#000000','SCROLL': '#10eaf0','BUTTON': ('#f1f4df', '#24009c'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#24009c', '#0028ff', '#10eaf0', '#f1f4df'],},
    'DarkBlue11': {'BACKGROUND': '#6384b3','TEXT': '#e6f0b6','INPUT': '#b8e9c0','TEXT_INPUT': '#000000','SCROLL': '#b8e9c0','BUTTON': ('#e6f0b6', '#684949'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#684949', '#6384b3', '#b8e9c0', '#e6f0b6'],},
    'DarkTeal8': {'BACKGROUND': '#71a0a5','TEXT': '#212121','INPUT': '#665c84','TEXT_INPUT': '#FFFFFF','SCROLL': '#212121','BUTTON': ('#fab95b', '#665c84'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#212121', '#665c84', '#71a0a5', '#fab95b'],},
    'DarkRed1': {'BACKGROUND': '#c10000','TEXT': '#eeeeee','INPUT': '#dedede','TEXT_INPUT': '#000000','SCROLL': '#dedede','BUTTON': ('#c10000', '#eeeeee'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#c10000', '#ff4949', '#dedede', '#eeeeee'],},
    'LightBrown5': {'BACKGROUND': '#fff591','TEXT': '#e41749','INPUT': '#f5587b','TEXT_INPUT': '#000000','SCROLL': '#e41749','BUTTON': ('#fff591', '#e41749'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#e41749', '#f5587b', '#ff8a5c', '#fff591'],},
    'LightGreen9': {'BACKGROUND': '#f1edb3','TEXT': '#3b503d','INPUT': '#4a746e','TEXT_INPUT': '#f1edb3','SCROLL': '#3b503d','BUTTON': ('#f1edb3', '#3b503d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3b503d', '#4a746e', '#c8cf94', '#f1edb3'],'DESCRIPTION': ['Green', 'Turquoise', 'Yellow'],},
    'DarkGreen2': {'BACKGROUND': '#3b503d','TEXT': '#f1edb3','INPUT': '#c8cf94','TEXT_INPUT': '#000000','SCROLL': '#c8cf94','BUTTON': ('#f1edb3', '#3b503d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3b503d', '#4a746e', '#c8cf94', '#f1edb3'],'DESCRIPTION': ['Green', 'Turquoise', 'Yellow'],},
    'LightGray1': {'BACKGROUND': '#f2f2f2','TEXT': '#222831','INPUT': '#393e46','TEXT_INPUT': '#FFFFFF','SCROLL': '#222831','BUTTON': ('#f2f2f2', '#222831'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#222831', '#393e46', '#f96d00', '#f2f2f2'],'DESCRIPTION': ['#000000', 'Grey', 'Orange', 'Grey', 'Autumn'],},
    'DarkGrey4': {'BACKGROUND': '#52524e','TEXT': '#e9e9e5','INPUT': '#d4d6c8','TEXT_INPUT': '#000000','SCROLL': '#d4d6c8','BUTTON': ('#FFFFFF', '#9a9b94'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#52524e', '#9a9b94', '#d4d6c8', '#e9e9e5'],'DESCRIPTION': ['Grey', 'Pastel', 'Winter'],},
    'DarkBlue12': {'BACKGROUND': '#324e7b','TEXT': '#f8f8f8','INPUT': '#86a6df','TEXT_INPUT': '#000000','SCROLL': '#86a6df','BUTTON': ('#FFFFFF', '#5068a9'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#324e7b', '#5068a9', '#86a6df', '#f8f8f8'],'DESCRIPTION': ['Blue', 'Grey', 'Cold', 'Winter'],},
    'DarkPurple6': {'BACKGROUND': '#070739','TEXT': '#e1e099','INPUT': '#c327ab','TEXT_INPUT': '#e1e099','SCROLL': '#c327ab','BUTTON': ('#e1e099', '#521477'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#070739', '#521477', '#c327ab', '#e1e099'],'DESCRIPTION': ['#000000', 'Purple', 'Yellow', 'Dark'],},
    'DarkPurple7': {'BACKGROUND': '#191930','TEXT': '#B1B7C5','INPUT': '#232B5C','TEXT_INPUT': '#D0E3E7','SCROLL': '#B1B7C5','BUTTON': ('#272D38', '#B1B7C5'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBlue13': {'BACKGROUND': '#203562','TEXT': '#e3e8f8','INPUT': '#c0c5cd','TEXT_INPUT': '#000000','SCROLL': '#c0c5cd','BUTTON': ('#FFFFFF', '#3e588f'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#203562', '#3e588f', '#c0c5cd', '#e3e8f8'],'DESCRIPTION': ['Blue', 'Grey', 'Wedding', 'Cold'],},
    'DarkBrown5': {'BACKGROUND': '#3c1b1f','TEXT': '#f6e1b5','INPUT': '#e2bf81','TEXT_INPUT': '#000000','SCROLL': '#e2bf81','BUTTON': ('#3c1b1f', '#f6e1b5'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3c1b1f', '#b21e4b', '#e2bf81', '#f6e1b5'],'DESCRIPTION': ['Brown', 'Red', 'Yellow', 'Warm'],},
    'DarkGreen3': {'BACKGROUND': '#062121','TEXT': '#eeeeee','INPUT': '#e4dcad','TEXT_INPUT': '#000000','SCROLL': '#e4dcad','BUTTON': ('#eeeeee', '#181810'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#062121', '#181810', '#e4dcad', '#eeeeee'],'DESCRIPTION': ['#000000', '#000000', 'Brown', 'Grey'],},
    'DarkBlack1': {'BACKGROUND': '#181810','TEXT': '#eeeeee','INPUT': '#e4dcad','TEXT_INPUT': '#000000','SCROLL': '#e4dcad','BUTTON': ('#FFFFFF', '#062121'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#062121', '#181810', '#e4dcad', '#eeeeee'],'DESCRIPTION': ['#000000', '#000000', 'Brown', 'Grey'],},
    'DarkGrey5': {'BACKGROUND': '#343434','TEXT': '#f3f3f3','INPUT': '#e9dcbe','TEXT_INPUT': '#000000','SCROLL': '#e9dcbe','BUTTON': ('#FFFFFF', '#8e8b82'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#343434', '#8e8b82', '#e9dcbe', '#f3f3f3'],'DESCRIPTION': ['Grey', 'Brown'],},
    'LightBrown12': {'BACKGROUND': '#8e8b82','TEXT': '#f3f3f3','INPUT': '#e9dcbe','TEXT_INPUT': '#000000','SCROLL': '#e9dcbe','BUTTON': ('#f3f3f3', '#8e8b82'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#343434', '#8e8b82', '#e9dcbe', '#f3f3f3'],'DESCRIPTION': ['Grey', 'Brown'],},
    'DarkTeal9': {'BACKGROUND': '#13445a','TEXT': '#fef4e8','INPUT': '#446878','TEXT_INPUT': '#FFFFFF','SCROLL': '#446878','BUTTON': ('#fef4e8', '#446878'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#13445a', '#970747', '#446878', '#fef4e8'],'DESCRIPTION': ['Red', 'Grey', 'Blue', 'Wedding', 'Retro'],},
    'DarkBlue14': {'BACKGROUND': '#21273d','TEXT': '#f1f6f8','INPUT': '#b9d4f1','TEXT_INPUT': '#000000','SCROLL': '#b9d4f1','BUTTON': ('#FFFFFF', '#6a759b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#21273d', '#6a759b', '#b9d4f1', '#f1f6f8'],'DESCRIPTION': ['Blue', '#000000', 'Grey', 'Cold', 'Winter'],},
    'LightBlue6': {'BACKGROUND': '#f1f6f8','TEXT': '#21273d','INPUT': '#6a759b','TEXT_INPUT': '#FFFFFF','SCROLL': '#21273d','BUTTON': ('#f1f6f8', '#6a759b'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#21273d', '#6a759b', '#b9d4f1', '#f1f6f8'],'DESCRIPTION': ['Blue', '#000000', 'Grey', 'Cold', 'Winter'],},
    'DarkGreen4': {'BACKGROUND': '#044343','TEXT': '#e4e4e4','INPUT': '#045757','TEXT_INPUT': '#e4e4e4','SCROLL': '#045757','BUTTON': ('#e4e4e4', '#045757'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#222222', '#044343', '#045757', '#e4e4e4'],'DESCRIPTION': ['#000000', 'Turquoise', 'Grey', 'Dark'],},
    'DarkGreen5': {'BACKGROUND': '#1b4b36','TEXT': '#e0e7f1','INPUT': '#aebd77','TEXT_INPUT': '#000000','SCROLL': '#aebd77','BUTTON': ('#FFFFFF', '#538f6a'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#1b4b36', '#538f6a', '#aebd77', '#e0e7f1'],'DESCRIPTION': ['Green', 'Grey'],},
    'DarkTeal10': {'BACKGROUND': '#0d3446','TEXT': '#d8dfe2','INPUT': '#71adb5','TEXT_INPUT': '#000000','SCROLL': '#71adb5','BUTTON': ('#FFFFFF', '#176d81'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#0d3446', '#176d81', '#71adb5', '#d8dfe2'],'DESCRIPTION': ['Grey', 'Turquoise', 'Winter', 'Cold'],},
    'DarkGrey6': {'BACKGROUND': '#3e3e3e','TEXT': '#ededed','INPUT': '#68868c','TEXT_INPUT': '#ededed','SCROLL': '#68868c','BUTTON': ('#FFFFFF', '#405559'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3e3e3e', '#405559', '#68868c', '#ededed'],'DESCRIPTION': ['Grey', 'Turquoise', 'Winter'],},
    'DarkTeal11': {'BACKGROUND': '#405559','TEXT': '#ededed','INPUT': '#68868c','TEXT_INPUT': '#ededed','SCROLL': '#68868c','BUTTON': ('#ededed', '#68868c'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#3e3e3e', '#405559', '#68868c', '#ededed'],'DESCRIPTION': ['Grey', 'Turquoise', 'Winter'],},
    'LightBlue7': {'BACKGROUND': '#9ed0e0','TEXT': '#19483f','INPUT': '#5c868e','TEXT_INPUT': '#FFFFFF','SCROLL': '#19483f','BUTTON': ('#FFFFFF', '#19483f'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#19483f', '#5c868e', '#ff6a38', '#9ed0e0'],'DESCRIPTION': ['Orange', 'Blue', 'Turquoise'],},
    'LightGreen10': {'BACKGROUND': '#d8ebb5','TEXT': '#205d67','INPUT': '#639a67','TEXT_INPUT': '#FFFFFF','SCROLL': '#205d67','BUTTON': ('#d8ebb5', '#205d67'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#205d67', '#639a67', '#d9bf77', '#d8ebb5'],'DESCRIPTION': ['Blue', 'Green', 'Brown', 'Vintage'],},
    'DarkBlue15': {'BACKGROUND': '#151680','TEXT': '#f1fea4','INPUT': '#375fc0','TEXT_INPUT': '#f1fea4','SCROLL': '#375fc0','BUTTON': ('#f1fea4', '#1c44ac'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#151680', '#1c44ac', '#375fc0', '#f1fea4'],'DESCRIPTION': ['Blue', 'Yellow', 'Cold'],},
    'DarkBlue16': {'BACKGROUND': '#1c44ac','TEXT': '#f1fea4','INPUT': '#375fc0','TEXT_INPUT': '#f1fea4','SCROLL': '#375fc0','BUTTON': ('#f1fea4', '#151680'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#151680', '#1c44ac', '#375fc0', '#f1fea4'],'DESCRIPTION': ['Blue', 'Yellow', 'Cold'],},
    'DarkTeal12': {'BACKGROUND': '#004a7c','TEXT': '#fafafa','INPUT': '#e8f1f5','TEXT_INPUT': '#000000','SCROLL': '#e8f1f5','BUTTON': ('#fafafa', '#005691'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#004a7c', '#005691', '#e8f1f5', '#fafafa'],'DESCRIPTION': ['Grey', 'Blue', 'Cold', 'Winter'],},
    'LightBrown13': {'BACKGROUND': '#ebf5ee','TEXT': '#921224','INPUT': '#bdc6b8','TEXT_INPUT': '#921224','SCROLL': '#921224','BUTTON': ('#FFFFFF', '#921224'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#921224', '#bdc6b8', '#bce0da', '#ebf5ee'],'DESCRIPTION': ['Red', 'Blue', 'Grey', 'Vintage', 'Wedding'],},
    'DarkBlue17': {'BACKGROUND': '#21294c','TEXT': '#f9f2d7','INPUT': '#f2dea8','TEXT_INPUT': '#000000','SCROLL': '#f2dea8','BUTTON': ('#f9f2d7', '#141829'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#141829', '#21294c', '#f2dea8', '#f9f2d7'],'DESCRIPTION': ['#000000', 'Blue', 'Yellow'],},
    'DarkBlue18': {'BACKGROUND': '#0c1825','TEXT': '#d1d7dd','INPUT': '#001c35','TEXT_INPUT': '#d1d7dd','SCROLL': '#00438e','BUTTON': ('#75b7ff', '#001c35'),'PROGRESS': ('#0074ff', '#75b7ff'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#141829', '#21294c', '#f2dea8', '#f9f2d7'],'DESCRIPTION': ['#000000', 'Blue', 'Yellow'],},
    'DarkBrown6': {'BACKGROUND': '#785e4d','TEXT': '#f2eee3','INPUT': '#baaf92','TEXT_INPUT': '#000000','SCROLL': '#baaf92','BUTTON': ('#FFFFFF', '#785e4d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#785e4d', '#ff8426', '#baaf92', '#f2eee3'],'DESCRIPTION': ['Grey', 'Brown', 'Orange', 'Autumn'],},
    'DarkGreen6': {'BACKGROUND': '#5c715e','TEXT': '#f2f9f1','INPUT': '#ddeedf','TEXT_INPUT': '#000000','SCROLL': '#ddeedf','BUTTON': ('#f2f9f1', '#5c715e'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#5c715e', '#b6cdbd', '#ddeedf', '#f2f9f1'],'DESCRIPTION': ['Grey', 'Green', 'Vintage'],},
    'DarkGreen7': {'BACKGROUND': '#0C231E','TEXT': '#efbe1c','INPUT': '#153C33','TEXT_INPUT': '#efbe1c','SCROLL': '#153C33','BUTTON': ('#efbe1c', '#153C33'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey7': {'BACKGROUND': '#4b586e','TEXT': '#dddddd','INPUT': '#574e6d','TEXT_INPUT': '#dddddd','SCROLL': '#574e6d','BUTTON': ('#dddddd', '#43405d'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#43405d', '#4b586e', '#574e6d', '#dddddd'],'DESCRIPTION': ['Grey', 'Winter', 'Cold'],},
    'DarkRed2': {'BACKGROUND': '#ab1212','TEXT': '#f6e4b5','INPUT': '#cd3131','TEXT_INPUT': '#f6e4b5','SCROLL': '#cd3131','BUTTON': ('#f6e4b5', '#ab1212'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#ab1212', '#1fad9f', '#cd3131', '#f6e4b5'],'DESCRIPTION': ['Turquoise', 'Red', 'Yellow'],},
    'LightGrey6': {'BACKGROUND': '#e3e3e3','TEXT': '#233142','INPUT': '#455d7a','TEXT_INPUT': '#e3e3e3','SCROLL': '#233142','BUTTON': ('#e3e3e3', '#455d7a'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,'COLOR_LIST': ['#233142', '#455d7a', '#f95959', '#e3e3e3'],'DESCRIPTION': ['#000000', 'Blue', 'Red', 'Grey'],},
    'HotDogStand': {'BACKGROUND': 'red','TEXT': 'yellow','INPUT': 'yellow','TEXT_INPUT': '#000000','SCROLL': 'yellow','BUTTON': ('red', 'yellow'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey8': {'BACKGROUND': '#19232D','TEXT': '#ffffff','INPUT': '#32414B','TEXT_INPUT': '#ffffff','SCROLL': '#505F69','BUTTON': ('#ffffff', '#32414B'),'PROGRESS': ('#505F69', '#32414B'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey9': {'BACKGROUND': '#36393F','TEXT': '#DCDDDE','INPUT': '#40444B','TEXT_INPUT': '#ffffff','SCROLL': '#202225','BUTTON': ('#202225', '#B9BBBE'),'PROGRESS': ('#202225', '#40444B'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey10': {'BACKGROUND': '#1c1e23','TEXT': '#cccdcf','INPUT': '#272a31','TEXT_INPUT': '#8b9fde','SCROLL': '#313641','BUTTON': ('#f5f5f6', '#2e3d5a'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey11': {'BACKGROUND': '#1c1e23','TEXT': '#cccdcf','INPUT': '#313641','TEXT_INPUT': '#cccdcf','SCROLL': '#313641','BUTTON': ('#f5f5f6', '#313641'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey12': {'BACKGROUND': '#1c1e23','TEXT': '#8b9fde','INPUT': '#313641','TEXT_INPUT': '#8b9fde','SCROLL': '#313641','BUTTON': ('#cccdcf', '#2e3d5a'),'PROGRESS': DEFAULT_PROGRESS_BAR_COMPUTE,'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey13': {'BACKGROUND': '#1c1e23','TEXT': '#cccdcf','INPUT': '#272a31','TEXT_INPUT': '#cccdcf','SCROLL': '#313641','BUTTON': ('#8b9fde', '#313641'),'PROGRESS': ('#cccdcf', '#272a31'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey14': {'BACKGROUND': '#24292e','TEXT': '#fafbfc','INPUT': '#1d2125','TEXT_INPUT': '#fafbfc','SCROLL': '#1d2125','BUTTON': ('#fafbfc', '#155398'),'PROGRESS': ('#155398', '#1d2125'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey15': {'BACKGROUND': '#121212','TEXT': '#dddddd','INPUT': '#1e1e1e','TEXT_INPUT': '#69b1ef','SCROLL': '#272727','BUTTON': ('#69b1ef', '#2e2e2e'),'PROGRESS': ('#69b1ef', '#2e2e2e'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkGrey16': {'BACKGROUND': '#353535','TEXT': '#ffffff','INPUT': '#191919','TEXT_INPUT': '#ffffff','SCROLL': '#454545','BUTTON': ('#ffffff', '#454545'),'PROGRESS': ('#757575', '#454545'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'DarkBrown7': {'BACKGROUND': '#2c2417','TEXT': '#baa379','INPUT': '#baa379','TEXT_INPUT': '#000000','SCROLL': '#392e1c','BUTTON': ('#000000', '#baa379'),'PROGRESS': ('#baa379', '#453923'),'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'Python': {'BACKGROUND': '#3d7aab','TEXT': '#ffde56','INPUT': '#295273','TEXT_INPUT': '#ffde56','SCROLL': '#295273','BUTTON': ('#ffde56', '#295273'),'PROGRESS': ('#ffde56', '#295273'),'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'PythonPlus': {'BACKGROUND': '#001d3c','TEXT': '#ffffff','INPUT': '#015bbb','TEXT_INPUT': '#fed500','SCROLL': '#015bbb','BUTTON': ('#fed500', '#015bbb'),'PROGRESS': ('#015bbb', '#fed500'),'BORDER': 1,'SLIDER_DEPTH': 1,'PROGRESS_DEPTH': 0,},
    'NeonBlue1': {'BACKGROUND': '#000000','TEXT': '#ffffff','INPUT': '#000000','TEXT_INPUT': '#33ccff','SCROLL': '#33ccff','BUTTON': ('#33ccff', '#000000'),'PROGRESS': ('#33ccff', '#ffffff'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'NeonGreen1': {'BACKGROUND': '#000000','TEXT': '#ffffff','INPUT': '#000000','TEXT_INPUT': '#96ff7b','SCROLL': '#96ff7b','BUTTON': ('#96ff7b', '#000000'),'PROGRESS': ('#96ff7b', '#ffffff'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
    'NeonYellow1': {'BACKGROUND': '#000000','TEXT': '#ffffff','INPUT': '#000000','TEXT_INPUT': '#ffcf40','SCROLL': '#ffcf40','BUTTON': ('#ffcf40', '#000000'),'PROGRESS': ('#ffcf40', '#ffffff'),'BORDER': 1,'SLIDER_DEPTH': 0,'PROGRESS_DEPTH': 0,},
}
# fmt: on


def list_of_look_and_feel_values():
    """
    Get a list of the valid values to pass into your call to change_look_and_feel

    :return: list of valid string values
    :rtype:  List[str]
    """

    return sorted(list(LOOK_AND_FEEL_TABLE.keys()))


def theme(new_theme=None):
    """
    Sets / Gets the current Theme.  If none is specified then returns the current theme.
    This call replaces the ChangeLookAndFeel / change_look_and_feel call which only sets the theme.

    :param new_theme: the new theme name to use
    :type new_theme:  (str)
    :return:          the currently selected theme
    :rtype:           (str)
    """
    global TRANSPARENT_BUTTON

    if new_theme is not None:
        change_look_and_feel(new_theme)
        TRANSPARENT_BUTTON = (theme_background_color(), theme_background_color())
    return CURRENT_LOOK_AND_FEEL


def theme_background_color(color=None):
    """
    Sets/Returns the background color currently in use
    Used for Windows and containers (Column, Frame, Tab) and tables

    :param color: new background color to use (optional)
    :type color:  (str)
    :return:      color string of the background color currently in use
    :rtype:       (str)
    """
    if color is not None:
        set_options(background_color=color)
    return DEFAULT_BACKGROUND_COLOR


# This "constant" is misleading but rather than remove and break programs, will try this method instead
TRANSPARENT_BUTTON = (
    theme_background_color(),
    theme_background_color(),
)  # replaces an older version that had hardcoded numbers


def theme_element_background_color(color=None):
    """
    Sets/Returns the background color currently in use for all elements except containers

    :return: (str) - color string of the element background color currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(element_background_color=color)
    return DEFAULT_ELEMENT_BACKGROUND_COLOR


def theme_text_color(color=None):
    """
    Sets/Returns the text color currently in use

    :return: (str) - color string of the text color currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(text_color=color)
    return DEFAULT_TEXT_COLOR


def theme_text_element_background_color(color=None):
    """
    Sets/Returns the background color for text elements

    :return: (str) - color string of the text background color currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(text_element_background_color=color)
    return DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR


def theme_input_background_color(color=None):
    """
    Sets/Returns the input element background color currently in use

    :return: (str) - color string of the input element background color currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(input_elements_background_color=color)
    return DEFAULT_INPUT_ELEMENTS_COLOR


def theme_input_text_color(color=None):
    """
    Sets/Returns the input element entry color (not the text but the thing that's displaying the text)

    :return: (str) - color string of the input element color currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(input_text_color=color)
    return DEFAULT_INPUT_TEXT_COLOR


def theme_button_color(color=None):
    """
    Sets/Returns the button color currently in use

    :return: (str, str) - TUPLE with color strings of the button color currently in use (button text color, button background color)
    :rtype:  (str, str)
    """
    if color is not None:
        if color == COLOR_SYSTEM_DEFAULT:
            color_tuple = (COLOR_SYSTEM_DEFAULT, COLOR_SYSTEM_DEFAULT)
        else:
            color_tuple = button_color_to_tuple(color, (None, None))
        if color_tuple == (None, None):
            if not SUPPRESS_ERROR_POPUPS:
                popup_error('theme_button_color - bad color string passed in', color)
            else:
                print('** Badly formatted button color... not a tuple nor string **', color)
            set_options(button_color=color)  # go ahead and try with their string
        else:
            set_options(button_color=color_tuple)
    return DEFAULT_BUTTON_COLOR


def theme_button_color_background():
    """
    Returns the button color background currently in use. Note this function simple calls the theme_button_color
    function and splits apart the tuple

    :return: color string of the button color background currently in use
    :rtype:  (str)
    """
    return theme_button_color()[1]


def theme_button_color_text():
    """
    Returns the button color text currently in use.  Note this function simple calls the theme_button_color
    function and splits apart the tuple

    :return: color string of the button color text currently in use
    :rtype:  (str)
    """
    return theme_button_color()[0]


def theme_progress_bar_color(color=None):
    """
    Sets/Returns the progress bar colors by the current color theme

    :return: (str, str) - TUPLE with color strings of the ProgressBar color currently in use(button text color, button background color)
    :rtype:  (str, str)
    """
    if color is not None:
        set_options(progress_meter_color=color)
    return DEFAULT_PROGRESS_BAR_COLOR


def theme_slider_color(color=None):
    """
    Sets/Returns the slider color (used for sliders)

    :return: color string of the slider color currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(scrollbar_color=color)
    return DEFAULT_SCROLLBAR_COLOR


def theme_border_width(border_width=None):
    """
    Sets/Returns the border width currently in use
    Used by non ttk elements at the moment

    :return: border width currently in use
    :rtype:  (int)
    """
    if border_width is not None:
        set_options(border_width=border_width)
    return DEFAULT_BORDER_WIDTH


def theme_slider_border_width(border_width=None):
    """
    Sets/Returns the slider border width currently in use

    :return: border width currently in use for sliders
    :rtype:  (int)
    """
    if border_width is not None:
        set_options(slider_border_width=border_width)
    return DEFAULT_SLIDER_BORDER_WIDTH


def theme_progress_bar_border_width(border_width=None):
    """
    Sets/Returns the progress meter border width currently in use

    :return: border width currently in use for progress meters
    :rtype:  (int)
    """
    if border_width is not None:
        set_options(progress_meter_border_depth=border_width)
    return DEFAULT_PROGRESS_BAR_BORDER_WIDTH


def theme_element_text_color(color=None):
    """
    Sets/Returns the text color used by elements that have text as part of their display (Tables, Trees and Sliders)

    :return: color string currently in use
    :rtype:  (str)
    """
    if color is not None:
        set_options(element_text_color=color)
    return DEFAULT_ELEMENT_TEXT_COLOR


def theme_list():
    """
    Returns a sorted list of the currently available color themes

    :return: A sorted list of the currently available color themes
    :rtype:  List[str]
    """
    return list_of_look_and_feel_values()


def theme_add_new(new_theme_name, new_theme_dict):
    """
    Add a new theme to the dictionary of themes

    :param new_theme_name: text to display in element
    :type new_theme_name:  (str)
    :param new_theme_dict: text to display in element
    :type new_theme_dict:  (dict)
    """
    global LOOK_AND_FEEL_TABLE
    try:
        LOOK_AND_FEEL_TABLE[new_theme_name] = new_theme_dict
    except Exception as e:
        print(f'Exception during adding new theme {e}')


def theme_use_custom_titlebar():
    """
    Returns True if a custom titlebar will be / should be used.
    The setting is in the Global Settings window and can be overwridden
    using set_options call

    :return:        True if a custom titlebar / custom menubar should be used
    :rtype:         (bool)
    """
    if USE_CUSTOM_TITLEBAR is False:
        return False

    return USE_CUSTOM_TITLEBAR or pysimplegui_user_settings.get('-custom titlebar-', False)


def theme_global(new_theme=None):
    """
    Sets / Gets the global PySimpleGUI Theme.  If none is specified then returns the global theme from user settings.
    Note the theme must be a standard, built-in PySimpleGUI theme... not a user-created theme.

    :param new_theme: the new theme name to use
    :type new_theme:  (str)
    :return:          the currently selected theme
    :rtype:           (str)
    """
    if new_theme is not None:
        if new_theme not in theme_list():
            popup_error_with_traceback(
                'Cannot use custom themes with theme_global call',
                f'Your request to use theme {new_theme} cannot be performed.',
                'The PySimpleGUI Global User Settings are meant for PySimpleGUI standard items, not user config items',
                'You can use any of the many built-in themes instead or use your own UserSettings file to store your custom theme',
            )
            return pysimplegui_user_settings.get('-theme-', CURRENT_LOOK_AND_FEEL)
        pysimplegui_user_settings.set('-theme-', new_theme)
        theme(new_theme)
        return new_theme
    else:
        return pysimplegui_user_settings.get('-theme-', CURRENT_LOOK_AND_FEEL)


def theme_previewer(columns=12, scrollable=False, scroll_area_size=(None, None), search_string=None, location=(None, None)):
    """
    Displays a "Quick Reference Window" showing all of the different Look and Feel settings that are available.
    They are sorted alphabetically.  The legacy color names are mixed in, but otherwise they are sorted into Dark and Light halves
    If one of the "OK" buttons are pressed then that theme name is returned.

    :param columns:          The number of themes to display per row
    :type columns:           int
    :param scrollable:       If True then scrollbars will be added
    :type scrollable:        bool
    :param scroll_area_size: Size of the scrollable area (The Column Element used to make scrollable)
    :type scroll_area_size:  (int, int)
    :param search_string:    If specified then only themes containing this string will be shown
    :type search_string:     str
    :param location:         Location on the screen to place the window. Defaults to the center like all windows
    :type location:          (int, int)
    """

    current_theme = theme()

    # Show a "splash" type message so the user doesn't give up waiting
    popup_quick_message(
        'Hang on for a moment, this will take a bit to create....',
        keep_on_top=True,
        background_color='red',
        text_color='#FFFFFF',
        auto_close=True,
        non_blocking=True,
    )

    web = False

    win_bg = 'black'

    def sample_layout(theme_name):
        return [
            [Text('Text element'), InputText('Input data here', size=(10, 1))],
            [Button('Ok', key=f"choose_{theme_name}", tooltip=f"Choose {theme_name}"), Button('Disabled', disabled=True), Slider((1, 10), orientation='h', size=(5, 15))],
        ]

    names = list_of_look_and_feel_values()
    names.sort()
    if search_string not in (None, ''):
        names = [name for name in names if search_string.lower().replace(' ', '') in name.lower().replace(' ', '')]

    if search_string not in (None, ''):
        layout = [[Text(f'Themes containing "{search_string}"', font='Default 18', background_color=win_bg)]]
    else:
        layout = [[Text('List of all themes', font='Default 18', background_color=win_bg)]]

    col_layout = []
    row = []
    for count, theme_name in enumerate(names):
        theme(theme_name)
        if not count % columns:
            col_layout += [row]
            row = []
        row += [Frame(theme_name, sample_layout(theme_name) if not web else [[T(theme_name)]] + sample_layout(theme_name), pad=(2, 2))]
    if row:
        col_layout += [row]

    layout += [
        [
            Column(
                col_layout,
                scrollable=scrollable,
                size=scroll_area_size,
                pad=(0, 0),
                background_color=win_bg,
                key='-COL-',
            )
        ]
    ]
    window = Window(
        'Preview of Themes',
        layout,
        background_color=win_bg,
        resizable=True,
        location=location,
        keep_on_top=True,
        finalize=True,
        modal=True,
    )
    window['-COL-'].expand(True, True, True)  # needed so that col will expand with the window
    event, values = window.read(close=True)
    theme(current_theme)
    if event and event.startswith('choose_'):
        return event[7:]


preview_all_look_and_feel_themes = theme_previewer


def _theme_preview_window_swatches():
    # Begin the layout with a header
    layout = [
        [Text('Themes as color swatches', text_color='white', background_color='black', font='Default 25')],
        [
            Text(
                'Tooltip and right click a color to get the value',
                text_color='white',
                background_color='black',
                font='Default 15',
            )
        ],
        [
            Text(
                'Left click a color to copy to clipboard',
                text_color='white',
                background_color='black',
                font='Default 15',
            )
        ],
    ]
    layout = [[Column(layout, element_justification='c', background_color='black')]]
    # Create the pain part, the rows of Text with color swatches
    for i, theme_name in enumerate(theme_list()):
        theme(theme_name)
        colors = [
            theme_background_color(),
            theme_text_color(),
            theme_input_background_color(),
            theme_input_text_color(),
        ]
        if theme_button_color() != COLOR_SYSTEM_DEFAULT:
            colors.append(theme_button_color()[0])
            colors.append(theme_button_color()[1])
        colors = list(set(colors))  # de-duplicate items
        row = [T(theme(), background_color='black', text_color='white', size=(20, 1), justification='r')]
        for color in colors:
            if color != COLOR_SYSTEM_DEFAULT:
                row.append(
                    T(
                        SYMBOL_SQUARE,
                        text_color=color,
                        background_color='black',
                        pad=(0, 0),
                        font='DEFAUlT 20',
                        right_click_menu=['Nothing', [color]],
                        tooltip=color,
                        enable_events=True,
                        key=(i, color),
                    )
                )
        layout += [row]
    # place layout inside of a Column so that it's scrollable
    layout = [[Column(layout, size=(500, 900), scrollable=True, vertical_scroll_only=True, background_color='black')]]
    # finish the layout by adding an exit button
    layout += [[B('Exit')]]

    # create and return Window that uses the layout
    return Window('Theme Color Swatches', layout, background_color='black', finalize=True, keep_on_top=True)


def theme_previewer_swatches():
    """
    Display themes in a window as color swatches.
    Click on a color swatch to see the hex value printed on the console.
    If you hover over a color or right click it you'll also see the hext value.
    """
    current_theme = theme()
    popup_quick_message(
        'This is going to take a minute...',
        text_color='white',
        background_color='red',
        font='Default 20',
        keep_on_top=True,
    )
    window = _theme_preview_window_swatches()
    theme(OFFICIAL_PYSIMPLEGUI_THEME)
    # col_height = window.get_screen_size()[1]-200
    # if window.size[1] > 100:
    #     window.size = (window.size[0], col_height)
    # window.move(window.get_screen_size()[0] // 2 - window.size[0] // 2, 0)

    while True:  # Event Loop
        event, values = window.read()
        if event == WIN_CLOSED or event == 'Exit':
            break
        if isinstance(event, tuple):  # someone clicked a swatch
            chosen_color = event[1]
        else:
            if event[0] == '#':  # someone right clicked
                chosen_color = event
            else:
                chosen_color = ''
        print('Copied to clipboard color = ', chosen_color)
        clipboard_set(chosen_color)
        # window.TKroot.clipboard_clear()
        # window.TKroot.clipboard_append(chosen_color)
    window.close()
    theme(current_theme)


def change_look_and_feel(index, force=False):
    """
    Change the "color scheme" of all future PySimpleGUI Windows.
    The scheme are string names that specify a group of colors. Background colors, text colors, button colors.
    There are 13 different color settings that are changed at one time using a single call to ChangeLookAndFeel
    The look and feel table itself has these indexes into the dictionary LOOK_AND_FEEL_TABLE.
    The original list was (prior to a major rework and renaming)... these names still work...
    In Nov 2019 a new Theme Formula was devised to make choosing a theme easier:
    The "Formula" is:
    ["Dark" or "Light"] Color Number
    Colors can be Blue Brown Grey Green Purple Red Teal Yellow Black
    The number will vary for each pair. There are more DarkGrey entries than there are LightYellow for example.
    Default = The default settings (only button color is different than system default)
    Default1 = The full system default including the button (everything's gray... how sad... don't be all gray... please....)
    :param index: the name of the index into the Look and Feel table (does not have to be exact, can be "fuzzy")
    :type index:  (str)
    :param force: no longer used
    :type force:  (bool)
    :return:      None
    :rtype:       None
    """
    global CURRENT_LOOK_AND_FEEL

    # if running_mac() and not force:
    #     print('*** Changing look and feel is not supported on Mac platform ***')
    #     return

    requested_theme_name = index
    theme_names_list = list_of_look_and_feel_values()
    # normalize available l&f values by setting all to lower case
    lf_values_lowercase = [item.lower() for item in theme_names_list]
    # option 1
    opt1 = requested_theme_name.replace(' ', '').lower()
    # option 3 is option 1 with gray replaced with grey
    opt3 = opt1.replace('gray', 'grey')
    # option 2 (reverse lookup)
    optx = requested_theme_name.lower().split(' ')
    optx.reverse()
    opt2 = ''.join(optx)

    # search for valid l&f name
    if requested_theme_name in theme_names_list:
        ix = theme_names_list.index(requested_theme_name)
    elif opt1 in lf_values_lowercase:
        ix = lf_values_lowercase.index(opt1)
    elif opt2 in lf_values_lowercase:
        ix = lf_values_lowercase.index(opt2)
    elif opt3 in lf_values_lowercase:
        ix = lf_values_lowercase.index(opt3)
    else:
        ix = random.randint(0, len(lf_values_lowercase) - 1)
        print(f'** Warning - {index} Theme is not a valid theme. Change your theme call. **')
        print('valid values are', list_of_look_and_feel_values())
        print(f'Instead, please enjoy a random Theme named {list_of_look_and_feel_values()[ix]}')

    selection = theme_names_list[ix]
    CURRENT_LOOK_AND_FEEL = selection
    try:
        colors = LOOK_AND_FEEL_TABLE[selection]

        # Color the progress bar using button background and input colors...unless they're the same
        if colors['PROGRESS'] != COLOR_SYSTEM_DEFAULT:
            if colors['PROGRESS'] == DEFAULT_PROGRESS_BAR_COMPUTE:
                if colors['BUTTON'][1] != colors['INPUT'] and colors['BUTTON'][1] != colors['BACKGROUND']:
                    colors['PROGRESS'] = colors['BUTTON'][1], colors['INPUT']
                else:  # if the same, then use text input on top of input color
                    colors['PROGRESS'] = (colors['TEXT_INPUT'], colors['INPUT'])
        else:
            colors['PROGRESS'] = DEFAULT_PROGRESS_BAR_COLOR_OFFICIAL
        # call to change all the colors
        set_options(
            background_color=colors['BACKGROUND'],
            text_element_background_color=colors['BACKGROUND'],
            element_background_color=colors['BACKGROUND'],
            text_color=colors['TEXT'],
            input_elements_background_color=colors['INPUT'],
            # button_color=colors['BUTTON'] if not running_mac() else None,
            button_color=colors['BUTTON'],
            progress_meter_color=colors['PROGRESS'],
            border_width=colors['BORDER'],
            slider_border_width=colors['SLIDER_DEPTH'],
            progress_meter_border_depth=colors['PROGRESS_DEPTH'],
            scrollbar_color=(colors['SCROLL']),
            element_text_color=colors['TEXT'],
            input_text_color=colors['TEXT_INPUT'],
        )
    except:  # most likely an index out of range
        print('** Warning - Theme value not valid. Change your theme call. **')
        print('valid values are', list_of_look_and_feel_values())


# ------------------------ Color processing functions ------------------------


def _hex_to_hsl(hex):
    r, g, b = _hex_to_rgb(hex)
    return _rgb_to_hsl(r, g, b)


def _hex_to_rgb(hex):
    hex = hex.lstrip('#')
    hlen = len(hex)
    return tuple(int(hex[i : i + hlen // 3], 16) for i in range(0, hlen, hlen // 3))


def _rgb_to_hsl(r, g, b):
    r = float(r)
    g = float(g)
    b = float(b)
    high = max(r, g, b)
    low = min(r, g, b)
    h, s, v = ((high + low) / 2,) * 3
    if high == low:
        h = s = 0.0
    else:
        d = high - low
        lightness = (high + low) / 2
        s = d / (2 - high - low) if lightness > 0.5 else d / (high + low)
        h = {
            r: (g - b) / d + (6 if g < b else 0),
            g: (b - r) / d + 2,
            b: (r - g) / d + 4,
        }[high]
        h /= 6
    return h, s, v


def _hsl_to_rgb(h, s, l):  # noqa
    def hue_to_rgb(p, q, t):
        t += 1 if t < 0 else 0
        t -= 1 if t > 1 else 0
        if t < 1 / 6:
            return p + (q - p) * 6 * t
        if t < 1 / 2:
            return q
        if t < 2 / 3:
            p + (q - p) * (2 / 3 - t) * 6
        return p

    if s == 0:
        r, g, b = l, l, l
    else:
        q = l * (1 + s) if l < 0.5 else l + s - l * s
        p = 2 * l - q
        r = hue_to_rgb(p, q, h + 1 / 3)
        g = hue_to_rgb(p, q, h)
        b = hue_to_rgb(p, q, h - 1 / 3)

    return r, g, b


def _hsv_to_hsl(h, s, v):  # noqa
    lightness = 0.5 * v * (2 - s)
    s = v * s / (1 - fabs(2 * lightness - 1))
    return h, s, lightness


def _hsl_to_hsv(h, s, l):  # noqa
    v = (2 * l + s * (1 - fabs(2 * l - 1))) / 2
    s = 2 * (v - l) / v
    return h, s, v


# Converts an object's contents into a nice printable string.  Great for dumping debug data
def obj_to_string_single_obj(obj):
    """
    Dumps an Object's values as a formatted string.  Very nicely done. Great way to display an object's member variables in human form
    Returns only the top-most object's variables instead of drilling down to dispolay more
    :param obj: The object to display
    :type obj:  (Any)
    :return:    Formatted output of the object's values
    :rtype:     (str)
    """
    if obj is None:
        return 'None'
    return str(obj.__class__) + '\n' + '\n'.join(repr(item) + ' = ' + repr(obj.__dict__[item]) for item in sorted(obj.__dict__))


def obj_to_string(obj, extra='    '):
    """
    Dumps an Object's values as a formatted string.  Very nicely done. Great way to display an object's member variables in human form
    :param obj:   The object to display
    :type obj:    (Any)
    :param extra: extra stuff (Default value = '    ')
    :type extra:  (str)
    :return:      Formatted output of the object's values
    :rtype:       (str)
    """
    if obj is None:
        return 'None'
    return str(obj.__class__) + '\n' + '\n'.join(extra + (str(item) + ' = ' + (obj_to_string(obj.__dict__[item], extra + '    ') if hasattr(obj.__dict__[item], '__dict__') else str(obj.__dict__[item]))) for item in sorted(obj.__dict__))


# ...######..##.......####.########..########...#######.....###....########..########.
# ..##....##.##........##..##.....##.##.....##.##.....##...##.##...##.....##.##.....##
# ..##.......##........##..##.....##.##.....##.##.....##..##...##..##.....##.##.....##
# ..##.......##........##..########..########..##.....##.##.....##.########..##.....##
# ..##.......##........##..##........##.....##.##.....##.#########.##...##...##.....##
# ..##....##.##........##..##........##.....##.##.....##.##.....##.##....##..##.....##
# ...######..########.####.##........########...#######..##.....##.##.....##.########.
# ................................................................................
# ..########.##.....##.##....##..######..########.####..#######..##....##..######.
# ..##.......##.....##.###...##.##....##....##.....##..##.....##.###...##.##....##
# ..##.......##.....##.####..##.##..........##.....##..##.....##.####..##.##......
# ..######...##.....##.##.##.##.##..........##.....##..##.....##.##.##.##..######.
# ..##.......##.....##.##..####.##..........##.....##..##.....##.##..####.......##
# ..##.......##.....##.##...###.##....##....##.....##..##.....##.##...###.##....##
# ..##........#######..##....##..######.....##....####..#######..##....##..######.


def clipboard_set(new_value):
    """
    Sets the clipboard to a specific value.
    importANT NOTE - Your FreeSimpleGUI application needs to remain running until you've pasted
    your clipboard. This is a tkinter limitation.  A workaround was found for Windows, but you still
    need to stay running for Linux systems.

    :param new_value: value to set the clipboard to. Will be converted to a string
    :type new_value:  (str | bytes)
    """
    root = _get_hidden_master_root()
    root.clipboard_clear()
    root.clipboard_append(str(new_value))
    root.update()


def clipboard_get():
    """
    Gets the clipboard current value.

    :return: The current value of the clipboard
    :rtype:  (str)
    """
    root = _get_hidden_master_root()

    try:
        value = root.clipboard_get()
    except:
        value = ''
    root.update()
    return value


# MM"""""""`YM
# MM  mmmmm  M
# M'        .M .d8888b. 88d888b. dP    dP 88d888b. .d8888b.
# MM  MMMMMMMM 88'  `88 88'  `88 88    88 88'  `88 Y8ooooo.
# MM  MMMMMMMM 88.  .88 88.  .88 88.  .88 88.  .88       88
# MM  MMMMMMMM `88888P' 88Y888P' `88888P' 88Y888P' `88888P'
# MMMMMMMMMMMM          88                88
#                       dP                dP
# ------------------------------------------------------------------------------------------------------------------ #
# =====================================   Upper PySimpleGUI ======================================================== #
# ------------------------------------------------------------------------------------------------------------------ #
# ----------------------------------- The mighty Popup! ------------------------------------------------------------ #


def popup(
    *args,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    button_type=POPUP_BUTTONS_OK,
    auto_close=False,
    auto_close_duration=None,
    custom_text=(None, None),
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    any_key_closes=False,
    image=None,
    modal=True,
    button_justification=None,
    drop_whitespace=True,
):
    """
    Popup - Display a popup Window with as many parms as you wish to include.  This is the GUI equivalent of the
    "print" statement.  It's also great for "pausing" your program's flow until the user can read some error messages.

    If this popup doesn't have the features you want, then you can easily make your own. Popups can be accomplished in 1 line of code:
    choice, _ = sg.Window('Continue?', [[sg.T('Do you want to continue?')], [sg.Yes(s=10), sg.No(s=10)]], disable_close=True).read(close=True)


    :param *args:                 Variable number of your arguments.  Load up the call with stuff to see!
    :type *args:                  (Any)
    :param title:                 Optional title for the window. If none provided, the first arg will be used instead.
    :type title:                  (str)
    :param button_color:          Color of the buttons shown (text color, button color)
    :type button_color:           (str, str) | str
    :param background_color:      Window's background color
    :type background_color:       (str)
    :param text_color:            text color
    :type text_color:             (str)
    :param button_type:           NOT USER SET!  Determines which pre-defined buttons will be shown (Default value = POPUP_BUTTONS_OK). There are many Popup functions and they call Popup, changing this parameter to get the desired effect.
    :type button_type:            (int)
    :param auto_close:            If True the window will automatically close
    :type auto_close:             (bool)
    :param auto_close_duration:   time in seconds to keep window open before closing it automatically
    :type auto_close_duration:    (int)
    :param custom_text:           A string or pair of strings that contain the text to display on the buttons
    :type custom_text:            (str, str) | str
    :param non_blocking:          If True then will immediately return from the function without waiting for the user's input.
    :type non_blocking:           (bool)
    :param icon:                  icon to display on the window. Same format as a Window call
    :type icon:                   str | bytes
    :param line_width:            Width of lines in characters.  Defaults to MESSAGE_BOX_LINE_WIDTH
    :type line_width:             (int)
    :param font:                  specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                   str | Tuple[font_name, size, modifiers]
    :param no_titlebar:           If True will not show the frame around the window and the titlebar across the top
    :type no_titlebar:            (bool)
    :param grab_anywhere:         If True can grab anywhere to move the window. If no_titlebar is True, grab_anywhere should likely be enabled too
    :type grab_anywhere:          (bool)
    :param location:              Location on screen to display the top left corner of window. Defaults to window centered on screen
    :type location:               (int, int)
    :param relative_location:     (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:      (int, int)
    :param keep_on_top:           If True the window will remain above all current windows
    :type keep_on_top:            (bool)
    :param any_key_closes:        If True then will turn on return_keyboard_events for the window which will cause window to close as soon as any key is pressed.  Normally the return key only will close the window.  Default is false.
    :type any_key_closes:         (bool)
    :param image:                 Image to include at the top of the popup window
    :type image:                  (str) or (bytes)
    :param modal:                 If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                  bool
    :param right_justify_buttons: If True then the buttons will be "pushed" to the right side of the Window
    :type right_justify_buttons:  bool
    :param button_justification:  Speficies if buttons should be left, right or centered. Default is left justified
    :type button_justification:   str
    :param drop_whitespace:       Controls is whitespace should be removed when wrapping text.  Parameter is passed to textwrap.fill. Default is to drop whitespace (so popup remains backward compatible)
    :type drop_whitespace:        bool
    :return:                      Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                       str | None
    """

    if not args:
        args_to_print = ['']
    else:
        args_to_print = args
    if line_width is not None:
        local_line_width = line_width
    else:
        local_line_width = MESSAGE_BOX_LINE_WIDTH
    _title = title if title is not None else args_to_print[0]

    layout = [[]]
    max_line_total, total_lines = 0, 0
    if image is not None:
        if isinstance(image, str):
            layout += [[Image(filename=image)]]
        else:
            layout += [[Image(data=image)]]

    for message in args_to_print:
        # fancy code to check if string and convert if not is not need. Just always convert to string :-)
        # if not isinstance(message, str): message = str(message)
        message = str(message)
        if message.count('\n'):  # if there are line breaks, then wrap each segment separately
            # message_wrapped = message         # used to just do this, but now breaking into smaller pieces
            message_wrapped = ''
            msg_list = message.split('\n')  # break into segments that will each be wrapped
            message_wrapped = '\n'.join([textwrap.fill(msg, local_line_width) for msg in msg_list])
        else:
            message_wrapped = textwrap.fill(message, local_line_width, drop_whitespace=drop_whitespace)
        message_wrapped_lines = message_wrapped.count('\n') + 1
        longest_line_len = max([len(line) for line in message.split('\n')])
        width_used = min(longest_line_len, local_line_width)
        max_line_total = max(max_line_total, width_used)
        # height = _GetNumLinesNeeded(message, width_used)
        height = message_wrapped_lines
        layout += [[Text(message_wrapped, auto_size_text=True, text_color=text_color, background_color=background_color)]]
        total_lines += height

    if non_blocking:
        PopupButton = DummyButton  # important to use or else button will close other windows too!
    else:
        PopupButton = Button
    # show either an OK or Yes/No depending on paramater
    if custom_text != (None, None):
        if type(custom_text) is not tuple:
            layout += [
                [
                    PopupButton(
                        custom_text,
                        size=(len(custom_text), 1),
                        button_color=button_color,
                        focus=True,
                        bind_return_key=True,
                    )
                ]
            ]
        elif custom_text[1] is None:
            layout += [
                [
                    PopupButton(
                        custom_text[0],
                        size=(len(custom_text[0]), 1),
                        button_color=button_color,
                        focus=True,
                        bind_return_key=True,
                    )
                ]
            ]
        else:
            layout += [
                [
                    PopupButton(
                        custom_text[0],
                        button_color=button_color,
                        focus=True,
                        bind_return_key=True,
                        size=(len(custom_text[0]), 1),
                    ),
                    PopupButton(custom_text[1], button_color=button_color, size=(len(custom_text[1]), 1)),
                ]
            ]
    elif button_type == POPUP_BUTTONS_YES_NO:
        layout += [
            [
                PopupButton('Yes', button_color=button_color, focus=True, bind_return_key=True, size=(5, 1)),
                PopupButton('No', button_color=button_color, size=(5, 1)),
            ]
        ]
    elif button_type == POPUP_BUTTONS_CANCELLED:
        layout += [[PopupButton('Cancelled', button_color=button_color, focus=True, bind_return_key=True)]]
    elif button_type == POPUP_BUTTONS_ERROR:
        layout += [[PopupButton('Error', size=(6, 1), button_color=button_color, focus=True, bind_return_key=True)]]
    elif button_type == POPUP_BUTTONS_OK_CANCEL:
        layout += [
            [
                PopupButton('OK', size=(6, 1), button_color=button_color, focus=True, bind_return_key=True),
                PopupButton('Cancel', size=(6, 1), button_color=button_color),
            ]
        ]
    elif button_type == POPUP_BUTTONS_NO_BUTTONS:
        pass
    else:
        layout += [
            [
                PopupButton(
                    'OK',
                    size=(5, 1),
                    button_color=button_color,
                    focus=True,
                    bind_return_key=True,
                )
            ]
        ]
    if button_justification is not None:
        justification = button_justification.lower()[0]
        if justification == 'r':
            layout[-1] = [Push()] + layout[-1]
        elif justification == 'c':
            layout[-1] = [Push()] + layout[-1] + [Push()]

    window = Window(
        _title,
        layout,
        auto_size_text=True,
        background_color=background_color,
        button_color=button_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        icon=icon,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        return_keyboard_events=any_key_closes,
        modal=modal,
    )

    if non_blocking:
        button, values = window.read(timeout=0)
    else:
        button, values = window.read()
        window.close()
        del window

    return button


# ==============================  MsgBox============#
# Lazy function. Same as calling Popup with parms   #
# This function WILL Disappear perhaps today        #
# ==================================================#
# MsgBox is the legacy call and should not be used any longer
def MsgBox(*args):
    """
    Do not call this anymore it will raise exception.  Use Popups instead
    :param *args:
    :type *args:

    """
    raise DeprecationWarning('MsgBox is no longer supported... change your call to Popup')


# ========================  Scrolled Text Box   =====#
# ===================================================#
def popup_scrolled(
    *args,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    yes_no=False,
    no_buttons=False,
    button_justification='l',
    auto_close=False,
    auto_close_duration=None,
    size=(None, None),
    location=(None, None),
    relative_location=(None, None),
    non_blocking=False,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    font=None,
    image=None,
    icon=None,
    modal=True,
    no_sizegrip=False,
):
    """
    Show a scrolled Popup window containing the user's text that was supplied.  Use with as many items to print as you
    want, just like a print statement.

    :param *args:                 Variable number of items to display
    :type *args:                  (Any)
    :param title:                 Title to display in the window.
    :type title:                  (str)
    :param button_color:          button color (foreground, background)
    :type button_color:           (str, str) | str
    :param yes_no:                If True, displays Yes and No buttons instead of Ok
    :type yes_no:                 (bool)
    :param no_buttons:            If True, no buttons will be shown. User will have to close using the "X"
    :type no_buttons:             (bool)
    :param button_justification:  How buttons should be arranged.  l, c, r for Left, Center or Right justified
    :type button_justification:   (str)
    :param auto_close:            if True window will close itself
    :type auto_close:             (bool)
    :param auto_close_duration:   Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:    int | float
    :param size:                  (w,h) w=characters-wide, h=rows-high
    :type size:                   (int, int)
    :param location:              Location on the screen to place the upper left corner of the window
    :type location:               (int, int)
    :param relative_location:     (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:      (int, int)
    :param non_blocking:          if True the call will immediately return rather than waiting on user input
    :type non_blocking:           (bool)
    :param background_color:      color of background
    :type background_color:       (str)
    :param text_color:            color of the text
    :type text_color:             (str)
    :param no_titlebar:           If True no titlebar will be shown
    :type no_titlebar:            (bool)
    :param grab_anywhere:         If True, than can grab anywhere to move the window (Default = False)
    :type grab_anywhere:          (bool)
    :param keep_on_top:           If True the window will remain above all current windows
    :type keep_on_top:            (bool)
    :param font:                  specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                   (str or (str, int[, str]) or None)
    :param image:                 Image to include at the top of the popup window
    :type image:                  (str) or (bytes)
    :param icon:                  filename or base64 string to be used for the window's icon
    :type icon:                   bytes | str
    :param modal:                 If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                  bool
    :param no_sizegrip:           If True no Sizegrip will be shown when there is no titlebar. It's only shown if there is no titlebar
    :type no_sizegrip:            (bool)
    :return:                      Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                       str | None | TIMEOUT_KEY
    """

    if not args:
        return
    width, height = size
    width = width if width else MESSAGE_BOX_LINE_WIDTH

    layout = [[]]

    if image is not None:
        if isinstance(image, str):
            layout += [[Image(filename=image)]]
        else:
            layout += [[Image(data=image)]]
    max_line_total, max_line_width, total_lines, height_computed = 0, 0, 0, 0
    complete_output = ''
    for message in args:
        # fancy code to check if string and convert if not is not need. Just always convert to string :-)
        # if not isinstance(message, str): message = str(message)
        message = str(message)
        longest_line_len = max([len(line) for line in message.split('\n')])
        width_used = min(longest_line_len, width)
        max_line_total = max(max_line_total, width_used)
        max_line_width = width
        lines_needed = _GetNumLinesNeeded(message, width_used)
        height_computed += lines_needed + 1
        complete_output += message + '\n'
        total_lines += lines_needed
    height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed
    if height:
        height_computed = height
    layout += [
        [
            Multiline(
                complete_output,
                size=(max_line_width, height_computed),
                background_color=background_color,
                text_color=text_color,
                expand_x=True,
                expand_y=True,
                k='-MLINE-',
            )
        ]
    ]
    # show either an OK or Yes/No depending on paramater
    button = DummyButton if non_blocking else Button

    if yes_no:
        buttons = [button('Yes'), button('No')]
    elif no_buttons is not True:
        buttons = [button('OK', size=(5, 1), button_color=button_color)]
    else:
        buttons = None

    if buttons is not None:
        if button_justification.startswith('l'):
            layout += [buttons]
        elif button_justification.startswith('c'):
            layout += [[Push()] + buttons + [Push()]]
        else:
            layout += [[Push()] + buttons]

    if no_sizegrip is not True:
        layout[-1] += [Sizegrip()]

    window = Window(
        title or args[0],
        layout,
        auto_size_text=True,
        button_color=button_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        location=location,
        relative_location=relative_location,
        resizable=True,
        font=font,
        background_color=background_color,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        modal=modal,
        icon=icon,
    )
    if non_blocking:
        button, values = window.read(timeout=0)
    else:
        button, values = window.read()
        window.close()
        del window
    return button


# ============================== sprint ======#
# Is identical to the Scrolled Text Box       #
# Provides a crude 'print' mechanism but in a #
# GUI environment                             #
# This is in addition to the Print function   #
# which routes output to a "Debug Window"     #
# ============================================#


# --------------------------- popup_no_buttons ---------------------------
def popup_no_buttons(
    *args,
    title=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """Show a Popup but without any buttons

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        If True then will immediately return from the function without waiting for the user's input. (Default = False)
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True, than can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY"""
    popup(
        *args,
        title=title,
        background_color=background_color,
        text_color=text_color,
        button_type=POPUP_BUTTONS_NO_BUTTONS,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_non_blocking ---------------------------
def popup_non_blocking(
    *args,
    title=None,
    button_type=POPUP_BUTTONS_OK,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=True,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=False,
):
    """
    Show Popup window and immediately return (does not block)

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_type:         Determines which pre-defined buttons will be shown (Default value = POPUP_BUTTONS_OK).
    :type button_type:          (int)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = False
    :type modal:                bool
    :return:                    Reason for popup closing
    :rtype:                     str | None
    """

    return popup(
        *args,
        title=title,
        button_color=button_color,
        background_color=background_color,
        text_color=text_color,
        button_type=button_type,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_quick - a NonBlocking, Self-closing Popup  ---------------------------
def popup_quick(
    *args,
    title=None,
    button_type=POPUP_BUTTONS_OK,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=True,
    auto_close_duration=2,
    non_blocking=True,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=False,
):
    """
    Show Popup box that doesn't block and closes itself

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_type:         Determines which pre-defined buttons will be shown (Default value = POPUP_BUTTONS_OK).
    :type button_type:          (int)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = False
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """

    return popup(
        *args,
        title=title,
        button_color=button_color,
        background_color=background_color,
        text_color=text_color,
        button_type=button_type,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_quick_message - a NonBlocking, Self-closing Popup with no titlebar and no buttons ---------------------------
def popup_quick_message(
    *args,
    title=None,
    button_type=POPUP_BUTTONS_NO_BUTTONS,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=True,
    auto_close_duration=2,
    non_blocking=True,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=True,
    grab_anywhere=False,
    keep_on_top=True,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=False,
):
    """
    Show Popup window with no titlebar, doesn't block, and auto closes itself.

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_type:         Determines which pre-defined buttons will be shown (Default value = POPUP_BUTTONS_OK).
    :type button_type:          (int)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = False
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """
    return popup(
        *args,
        title=title,
        button_color=button_color,
        background_color=background_color,
        text_color=text_color,
        button_type=button_type,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- PopupNoTitlebar ---------------------------
def popup_no_titlebar(
    *args,
    title=None,
    button_type=POPUP_BUTTONS_OK,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    grab_anywhere=True,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """
    Display a Popup without a titlebar.   Enables grab anywhere so you can move it

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_type:         Determines which pre-defined buttons will be shown (Default value = POPUP_BUTTONS_OK).
    :type button_type:          (int)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """
    return popup(
        *args,
        title=title,
        button_color=button_color,
        background_color=background_color,
        text_color=text_color,
        button_type=button_type,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        font=font,
        no_titlebar=True,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- PopupAutoClose ---------------------------
def popup_auto_close(
    *args,
    title=None,
    button_type=POPUP_BUTTONS_OK,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=True,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """Popup that closes itself after some time period

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_type:         Determines which pre-defined buttons will be shown (Default value = POPUP_BUTTONS_OK).
    :type button_type:          (int)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """

    return popup(
        *args,
        title=title,
        button_color=button_color,
        background_color=background_color,
        text_color=text_color,
        button_type=button_type,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_error ---------------------------
def popup_error(
    *args,
    title=None,
    button_color=(None, None),
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """
    Popup with colored button and 'Error' as button text

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """
    tbutton_color = DEFAULT_ERROR_BUTTON_COLOR if button_color == (None, None) else button_color
    return popup(
        *args,
        title=title,
        button_type=POPUP_BUTTONS_ERROR,
        background_color=background_color,
        text_color=text_color,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        button_color=tbutton_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_cancel ---------------------------
def popup_cancel(
    *args,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """
    Display Popup with "cancelled" button text

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """
    return popup(
        *args,
        title=title,
        button_type=POPUP_BUTTONS_CANCELLED,
        background_color=background_color,
        text_color=text_color,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        button_color=button_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_ok ---------------------------
def popup_ok(
    *args,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """
    Display Popup with OK button only

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    Returns text of the button that was pressed.  None will be returned if user closed window with X
    :rtype:                     str | None | TIMEOUT_KEY
    """
    return popup(
        *args,
        title=title,
        button_type=POPUP_BUTTONS_OK,
        background_color=background_color,
        text_color=text_color,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        button_color=button_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_ok_cancel ---------------------------
def popup_ok_cancel(
    *args,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=DEFAULT_WINDOW_ICON,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """
    Display popup with OK and Cancel buttons

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    clicked button
    :rtype:                     "OK" | "Cancel" | None
    """
    return popup(
        *args,
        title=title,
        button_type=POPUP_BUTTONS_OK_CANCEL,
        background_color=background_color,
        text_color=text_color,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        button_color=button_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


# --------------------------- popup_yes_no ---------------------------
def popup_yes_no(
    *args,
    title=None,
    button_color=None,
    background_color=None,
    text_color=None,
    auto_close=False,
    auto_close_duration=None,
    non_blocking=False,
    icon=None,
    line_width=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    modal=True,
):
    """
    Display Popup with Yes and No buttons

    :param *args:               Variable number of items to display
    :type *args:                (Any)
    :param title:               Title to display in the window.
    :type title:                (str)
    :param button_color:        button color (foreground, background)
    :type button_color:         (str, str) | str
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param auto_close:          if True window will close itself
    :type auto_close:           (bool)
    :param auto_close_duration: Older versions only accept int. Time in seconds until window will close
    :type auto_close_duration:  int | float
    :param non_blocking:        if True the call will immediately return rather than waiting on user input
    :type non_blocking:         (bool)
    :param icon:                filename or base64 string to be used for the window's icon
    :type icon:                 bytes | str
    :param line_width:          Width of lines in characters
    :type line_width:           (int)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 (str or (str, int[, str]) or None)
    :param no_titlebar:         If True no titlebar will be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True the window will remain above all current windows
    :type keep_on_top:          (bool)
    :param location:            Location of upper left corner of the window
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param image:               Image to include at the top of the popup window
    :type image:                (str) or (bytes)
    :param modal:               If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                bool
    :return:                    clicked button
    :rtype:                     "Yes" | "No" | None
    """
    return popup(
        *args,
        title=title,
        button_type=POPUP_BUTTONS_YES_NO,
        background_color=background_color,
        text_color=text_color,
        non_blocking=non_blocking,
        icon=icon,
        line_width=line_width,
        button_color=button_color,
        auto_close=auto_close,
        auto_close_duration=auto_close_duration,
        font=font,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        image=image,
        modal=modal,
    )


##############################################################################
#   The popup_get_____ functions - Will return user input                     #
##############################################################################

# --------------------------- popup_get_folder ---------------------------


def popup_get_folder(
    message,
    title=None,
    default_path='',
    no_window=False,
    size=(None, None),
    button_color=None,
    background_color=None,
    text_color=None,
    icon=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    initial_folder=None,
    image=None,
    modal=True,
    history=False,
    history_setting_filename=None,
):
    """
    Display popup with text entry field and browse button so that a folder can be chosen.

    :param message:                  message displayed to user
    :type message:                   (str)
    :param title:                    Window title
    :type title:                     (str)
    :param default_path:             path to display to user as starting point (filled into the input field)
    :type default_path:              (str)
    :param no_window:                if True, no PySimpleGUI window will be shown. Instead just the tkinter dialog is shown
    :type no_window:                 (bool)
    :param size:                     (width, height) of the InputText Element
    :type size:                      (int, int)
    :param button_color:             button color (foreground, background)
    :type button_color:              (str, str) | str
    :param background_color:         color of background
    :type background_color:          (str)
    :param text_color:               color of the text
    :type text_color:                (str)
    :param icon:                     filename or base64 string to be used for the window's icon
    :type icon:                      bytes | str
    :param font:                     specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                      (str or (str, int[, str]) or None)
    :param no_titlebar:              If True no titlebar will be shown
    :type no_titlebar:               (bool)
    :param grab_anywhere:            If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:             (bool)
    :param keep_on_top:              If True the window will remain above all current windows
    :type keep_on_top:               (bool)
    :param location:                 Location of upper left corner of the window
    :type location:                  (int, int)
    :param relative_location:        (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:         (int, int)
    :param initial_folder:           location in filesystem to begin browsing
    :type initial_folder:            (str)
    :param image:                    Image to include at the top of the popup window
    :type image:                     (str) or (bytes)
    :param modal:                    If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                     bool
    :param history:                  If True then enable a "history" feature that will display previous entries used. Uses settings filename provided or default if none provided
    :type history:                   bool
    :param history_setting_filename: Filename to use for the User Settings. Will store list of previous entries in this settings file
    :type history_setting_filename:  (str)
    :return:                         string representing the path chosen, None if cancelled or window closed with X
    :rtype:                          str | None
    """

    # First setup the history settings file if history feature is enabled
    if history and history_setting_filename is not None:
        try:
            history_settings = UserSettings(history_setting_filename)
        except Exception as e:
            _error_popup_with_traceback(
                'popup_get_folder - Something is wrong with your supplied history settings filename',
                f'Exception: {e}',
            )
            return None
    elif history:
        history_settings_filename = os.path.basename(inspect.stack()[1].filename)
        history_settings_filename = os.path.splitext(history_settings_filename)[0] + '.json'
        history_settings = UserSettings(history_settings_filename)
    else:
        history_settings = None

    # global _my_windows
    if no_window:
        _get_hidden_master_root()
        root = tk.Toplevel()

        try:
            root.attributes('-alpha', 0)  # hide window while building it. makes for smoother 'paint'
            # if not running_mac():
            try:
                root.wm_overrideredirect(True)
            except Exception as e:
                print('* Error performing wm_overrideredirect while hiding the window during creation in get folder *', e)
            root.withdraw()
        except:
            pass
        folder_name = tk.filedialog.askdirectory(initialdir=initial_folder)  # show the 'get folder' dialog box

        root.destroy()

        return folder_name

    browse_button = FolderBrowse(initial_folder=initial_folder)

    if image is not None:
        if isinstance(image, str):
            layout = [[Image(filename=image)]]
        else:
            layout = [[Image(data=image)]]
    else:
        layout = [[]]

    layout += [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)]]

    if not history:
        layout += [[InputText(default_text=default_path, size=size, key='-INPUT-'), browse_button]]
    else:
        file_list = history_settings.get('-PSG folder list-', [])
        last_entry = file_list[0] if file_list else ''
        layout += [
            [
                Combo(
                    file_list,
                    default_value=last_entry,
                    key='-INPUT-',
                    size=size if size != (None, None) else (80, 1),
                    bind_return_key=True,
                ),
                browse_button,
                Button('Clear History', tooltip='Clears the list of folders shown in the combobox'),
            ]
        ]

    layout += [[Button('Ok', size=(6, 1), bind_return_key=True), Button('Cancel', size=(6, 1))]]

    window = Window(
        title=title or message,
        layout=layout,
        icon=icon,
        auto_size_text=True,
        button_color=button_color,
        font=font,
        background_color=background_color,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        modal=modal,
    )

    while True:
        event, values = window.read()
        if event in ('Cancel', WIN_CLOSED):
            break
        elif event == 'Clear History':
            history_settings.set('-PSG folder list-', [])
            window['-INPUT-'].update('', [])
            popup_quick_message(
                'History of Previous Choices Cleared',
                background_color='red',
                text_color='white',
                font='_ 20',
                keep_on_top=True,
            )
        elif event in ('Ok', '-INPUT-'):
            if values['-INPUT-'] != '':
                if history_settings is not None:
                    list_of_entries = history_settings.get('-PSG folder list-', [])
                    if values['-INPUT-'] in list_of_entries:
                        list_of_entries.remove(values['-INPUT-'])
                    list_of_entries.insert(0, values['-INPUT-'])
                    history_settings.set('-PSG folder list-', list_of_entries)
            break

    window.close()
    del window
    if event in ('Cancel', WIN_CLOSED):
        return None

    return values['-INPUT-']


# --------------------------- popup_get_file ---------------------------


def popup_get_file(
    message,
    title=None,
    default_path='',
    default_extension='',
    save_as=False,
    multiple_files=False,
    file_types=FILE_TYPES_ALL_FILES,
    no_window=False,
    size=(None, None),
    button_color=None,
    background_color=None,
    text_color=None,
    icon=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    initial_folder=None,
    image=None,
    files_delimiter=BROWSE_FILES_DELIMITER,
    modal=True,
    history=False,
    show_hidden=True,
    history_setting_filename=None,
):
    """
    Display popup window with text entry field and browse button so that a file can be chosen by user.

    :param message:                  message displayed to user
    :type message:                   (str)
    :param title:                    Window title
    :type title:                     (str)
    :param default_path:             path to display to user as starting point (filled into the input field)
    :type default_path:              (str)
    :param default_extension:        If no extension entered by user, add this to filename (only used in saveas dialogs)
    :type default_extension:         (str)
    :param save_as:                  if True, the "save as" dialog is shown which will verify before overwriting
    :type save_as:                   (bool)
    :param multiple_files:           if True, then allows multiple files to be selected that are returned with ';' between each filename
    :type multiple_files:            (bool)
    :param file_types:               List of extensions to show using wildcards. All files (the default) = (("ALL Files", "*.* *"),).
    :type file_types:                Tuple[Tuple[str,str]]
    :param no_window:                if True, no PySimpleGUI window will be shown. Instead just the tkinter dialog is shown
    :type no_window:                 (bool)
    :param size:                     (width, height) of the InputText Element or Combo element if using history feature
    :type size:                      (int, int)
    :param button_color:             Color of the button (text, background)
    :type button_color:              (str, str) | str
    :param background_color:         background color of the entire window
    :type background_color:          (str)
    :param text_color:               color of the text
    :type text_color:                (str)
    :param icon:                     filename or base64 string to be used for the window's icon
    :type icon:                      bytes | str
    :param font:                     specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                      (str or (str, int[, str]) or None)
    :param no_titlebar:              If True no titlebar will be shown
    :type no_titlebar:               (bool)
    :param grab_anywhere:            If True: can grab anywhere to move the window (Default = False)
    :type grab_anywhere:             (bool)
    :param keep_on_top:              If True the window will remain above all current windows
    :type keep_on_top:               (bool)
    :param location:                 Location of upper left corner of the window
    :type location:                  (int, int)
    :param relative_location:        (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:         (int, int)
    :param initial_folder:           location in filesystem to begin browsing
    :type initial_folder:            (str)
    :param image:                    Image to include at the top of the popup window
    :type image:                     (str) or (bytes)
    :param files_delimiter:          String to place between files when multiple files are selected. Normally a ;
    :type files_delimiter:           str
    :param modal:                    If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                     bool
    :param history:                  If True then enable a "history" feature that will display previous entries used. Uses settings filename provided or default if none provided
    :type history:                   bool
    :param show_hidden:              If True then enables the checkbox in the system dialog to select hidden files to be shown
    :type show_hidden:               bool
    :param history_setting_filename: Filename to use for the User Settings. Will store list of previous entries in this settings file
    :type history_setting_filename:  (str)
    :return:                         string representing the file(s) chosen, None if cancelled or window closed with X
    :rtype:                          str | None
    """

    # First setup the history settings file if history feature is enabled
    if history and history_setting_filename is not None:
        try:
            history_settings = UserSettings(history_setting_filename)
        except Exception as e:
            _error_popup_with_traceback(
                'popup_get_file - Something is wrong with your supplied history settings filename',
                f'Exception: {e}',
            )
            return None
    elif history:
        history_settings_filename = os.path.basename(inspect.stack()[1].filename)
        history_settings_filename = os.path.splitext(history_settings_filename)[0] + '.json'
        history_settings = UserSettings(history_settings_filename)
    else:
        history_settings = None

    if icon is None:
        icon = Window._user_defined_icon or DEFAULT_BASE64_ICON
    if no_window:
        _get_hidden_master_root()
        root = tk.Toplevel()

        try:
            root.attributes('-alpha', 0)  # hide window while building it. makes for smoother 'paint'
            # if not running_mac():
            try:
                root.wm_overrideredirect(True)
            except Exception as e:
                print('* Error performing wm_overrideredirect in get file *', e)
            root.withdraw()
        except:
            pass

        if show_hidden is False:
            try:
                # call a dummy dialog with an impossible option to initialize the file
                # dialog without really getting a dialog window; this will throw a
                # TclError, so we need a try...except :
                try:
                    root.tk.call('tk_getOpenFile', '-foobarbaz')
                except tk.TclError:
                    pass
                # now set the magic variables accordingly
                root.tk.call('set', '::tk::dialog::file::showHiddenBtn', '1')
                root.tk.call('set', '::tk::dialog::file::showHiddenVar', '0')
            except:
                pass

        if root and icon is not None:
            _set_icon_for_tkinter_window(root, icon=icon)
        # for Macs, setting parent=None fixes a warning problem.
        if save_as:
            if running_mac():
                is_all = [(x, y) for (x, y) in file_types if all(ch in '* .' for ch in y)]
                if not len(set(file_types)) > 1 and (len(is_all) != 0 or file_types == FILE_TYPES_ALL_FILES):
                    filename = tk.filedialog.asksaveasfilename(initialdir=initial_folder, initialfile=default_path, defaultextension=default_extension)  # show the 'get file' dialog box
                else:
                    filename = tk.filedialog.asksaveasfilename(
                        filetypes=file_types,
                        initialdir=initial_folder,
                        initialfile=default_path,
                        defaultextension=default_extension,
                    )  # show the 'get file' dialog box
            else:
                filename = tk.filedialog.asksaveasfilename(
                    filetypes=file_types,
                    initialdir=initial_folder,
                    initialfile=default_path,
                    parent=root,
                    defaultextension=default_extension,
                )  # show the 'get file' dialog box
        elif multiple_files:
            if running_mac():
                is_all = [(x, y) for (x, y) in file_types if all(ch in '* .' for ch in y)]
                if not len(set(file_types)) > 1 and (len(is_all) != 0 or file_types == FILE_TYPES_ALL_FILES):
                    filename = tk.filedialog.askopenfilenames(initialdir=initial_folder, initialfile=default_path, defaultextension=default_extension)  # show the 'get file' dialog box
                else:
                    filename = tk.filedialog.askopenfilenames(
                        filetypes=file_types,
                        initialdir=initial_folder,
                        initialfile=default_path,
                        defaultextension=default_extension,
                    )  # show the 'get file' dialog box
            else:
                filename = tk.filedialog.askopenfilenames(
                    filetypes=file_types,
                    initialdir=initial_folder,
                    initialfile=default_path,
                    parent=root,
                    defaultextension=default_extension,
                )  # show the 'get file' dialog box
        else:
            if running_mac():
                is_all = [(x, y) for (x, y) in file_types if all(ch in '* .' for ch in y)]
                if not len(set(file_types)) > 1 and (len(is_all) != 0 or file_types == FILE_TYPES_ALL_FILES):
                    filename = tk.filedialog.askopenfilename(initialdir=initial_folder, initialfile=default_path, defaultextension=default_extension)  # show the 'get files' dialog box
                else:
                    filename = tk.filedialog.askopenfilename(
                        filetypes=file_types,
                        initialdir=initial_folder,
                        initialfile=default_path,
                        defaultextension=default_extension,
                    )  # show the 'get files' dialog box
            else:
                filename = tk.filedialog.askopenfilename(
                    filetypes=file_types,
                    initialdir=initial_folder,
                    initialfile=default_path,
                    parent=root,
                    defaultextension=default_extension,
                )  # show the 'get files' dialog box
        root.destroy()

        if not multiple_files and type(filename) in (tuple, list):
            if len(filename):  # only if not 0 length, otherwise will get an error
                filename = filename[0]
        if not filename:
            return None
        return filename

    if save_as:
        browse_button = SaveAs(file_types=file_types, initial_folder=initial_folder, default_extension=default_extension)
    elif multiple_files:
        browse_button = FilesBrowse(file_types=file_types, initial_folder=initial_folder, files_delimiter=files_delimiter)
    else:
        browse_button = FileBrowse(file_types=file_types, initial_folder=initial_folder)

    if image is not None:
        if isinstance(image, str):
            layout = [[Image(filename=image)]]
        else:
            layout = [[Image(data=image)]]
    else:
        layout = [[]]

    layout += [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)]]

    if not history:
        layout += [[InputText(default_text=default_path, size=size, key='-INPUT-'), browse_button]]
    else:
        file_list = history_settings.get('-PSG file list-', [])
        last_entry = file_list[0] if file_list else ''
        layout += [
            [
                Combo(
                    file_list,
                    default_value=last_entry,
                    key='-INPUT-',
                    size=size if size != (None, None) else (80, 1),
                    bind_return_key=True,
                ),
                browse_button,
                Button('Clear History', tooltip='Clears the list of files shown in the combobox'),
            ]
        ]

    layout += [[Button('Ok', size=(6, 1), bind_return_key=True), Button('Cancel', size=(6, 1))]]

    window = Window(
        title=title or message,
        layout=layout,
        icon=icon,
        auto_size_text=True,
        button_color=button_color,
        font=font,
        background_color=background_color,
        no_titlebar=no_titlebar,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        modal=modal,
        finalize=True,
    )

    if running_linux() and show_hidden is True:
        window.TKroot.tk.eval('catch {tk_getOpenFile -badoption}')  # dirty hack to force autoloading of Tk's file dialog code
        window.TKroot.setvar('::tk::dialog::file::showHiddenBtn', 1)  # enable the "show hidden files" checkbox (it's necessary)
        window.TKroot.setvar('::tk::dialog::file::showHiddenVar', 0)  # start with the hidden files... well... hidden

    while True:
        event, values = window.read()
        if event in ('Cancel', WIN_CLOSED):
            break
        elif event == 'Clear History':
            history_settings.set('-PSG file list-', [])
            window['-INPUT-'].update('', [])
            popup_quick_message(
                'History of Previous Choices Cleared',
                background_color='red',
                text_color='white',
                font='_ 20',
                keep_on_top=True,
            )
        elif event in ('Ok', '-INPUT-'):
            if values['-INPUT-'] != '':
                if history_settings is not None:
                    list_of_entries = history_settings.get('-PSG file list-', [])
                    if values['-INPUT-'] in list_of_entries:
                        list_of_entries.remove(values['-INPUT-'])
                    list_of_entries.insert(0, values['-INPUT-'])
                    history_settings.set('-PSG file list-', list_of_entries)
            break

    window.close()
    del window
    if event in ('Cancel', WIN_CLOSED):
        return None

    return values['-INPUT-']


# --------------------------- popup_get_text ---------------------------


def popup_get_text(
    message,
    title=None,
    default_text='',
    password_char='',
    size=(None, None),
    button_color=None,
    background_color=None,
    text_color=None,
    icon=None,
    font=None,
    no_titlebar=False,
    grab_anywhere=False,
    keep_on_top=None,
    location=(None, None),
    relative_location=(None, None),
    image=None,
    history=False,
    history_setting_filename=None,
    modal=True,
):
    """
    Display Popup with text entry field. Returns the text entered or None if closed / cancelled

    :param message:                  message displayed to user
    :type message:                   (str)
    :param title:                    Window title
    :type title:                     (str)
    :param default_text:             default value to put into input area
    :type default_text:              (str)
    :param password_char:            character to be shown instead of actually typed characters. WARNING - if history=True then can't hide passwords
    :type password_char:             (str)
    :param size:                     (width, height) of the InputText Element
    :type size:                      (int, int)
    :param button_color:             Color of the button (text, background)
    :type button_color:              (str, str) | str
    :param background_color:         background color of the entire window
    :type background_color:          (str)
    :param text_color:               color of the message text
    :type text_color:                (str)
    :param icon:                     filename or base64 string to be used for the window's icon
    :type icon:                      bytes | str
    :param font:                     specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                      (str or (str, int[, str]) or None)
    :param no_titlebar:              If True no titlebar will be shown
    :type no_titlebar:               (bool)
    :param grab_anywhere:            If True can click and drag anywhere in the window to move the window
    :type grab_anywhere:             (bool)
    :param keep_on_top:              If True the window will remain above all current windows
    :type keep_on_top:               (bool)
    :param location:                 (x,y) Location on screen to display the upper left corner of window
    :type location:                  (int, int)
    :param relative_location:        (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:         (int, int)
    :param image:                    Image to include at the top of the popup window
    :type image:                     (str) or (bytes)
    :param history:                  If True then enable a "history" feature that will display previous entries used. Uses settings filename provided or default if none provided
    :type history:                   bool
    :param history_setting_filename: Filename to use for the User Settings. Will store list of previous entries in this settings file
    :type history_setting_filename:  (str)
    :param modal:                    If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                     bool
    :return:                         Text entered or None if window was closed or cancel button clicked
    :rtype:                          str | None
    """

    # First setup the history settings file if history feature is enabled
    if history and history_setting_filename is not None:
        try:
            history_settings = UserSettings(history_setting_filename)
        except Exception as e:
            _error_popup_with_traceback(
                'popup_get_file - Something is wrong with your supplied history settings filename',
                f'Exception: {e}',
            )
            return None
    elif history:
        history_settings_filename = os.path.basename(inspect.stack()[1].filename)
        history_settings_filename = os.path.splitext(history_settings_filename)[0] + '.json'
        history_settings = UserSettings(history_settings_filename)
    else:
        history_settings = None

    if image is not None:
        if isinstance(image, str):
            layout = [[Image(filename=image)]]
        else:
            layout = [[Image(data=image)]]
    else:
        layout = [[]]

    layout += [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)]]
    if not history:
        layout += [[InputText(default_text=default_text, size=size, key='-INPUT-', password_char=password_char)]]
    else:
        text_list = history_settings.get('-PSG text list-', [])
        last_entry = text_list[0] if text_list else default_text
        layout += [
            [
                Combo(
                    text_list,
                    default_value=last_entry,
                    key='-INPUT-',
                    size=size if size != (None, None) else (80, 1),
                    bind_return_key=True,
                ),
                Button('Clear History', tooltip='Clears the list of files shown in the combobox'),
            ]
        ]

    layout += [[Button('Ok', size=(6, 1), bind_return_key=True), Button('Cancel', size=(6, 1))]]

    window = Window(
        title=title or message,
        layout=layout,
        icon=icon,
        auto_size_text=True,
        button_color=button_color,
        no_titlebar=no_titlebar,
        background_color=background_color,
        grab_anywhere=grab_anywhere,
        keep_on_top=keep_on_top,
        location=location,
        relative_location=relative_location,
        finalize=True,
        modal=modal,
        font=font,
    )

    while True:
        event, values = window.read()
        if event in ('Cancel', WIN_CLOSED):
            break
        elif event == 'Clear History':
            history_settings.set('-PSG text list-', [])
            window['-INPUT-'].update('', [])
            popup_quick_message(
                'History of Previous Choices Cleared',
                background_color='red',
                text_color='white',
                font='_ 20',
                keep_on_top=True,
            )
        elif event in ('Ok', '-INPUT-'):
            if values['-INPUT-'] != '':
                if history_settings is not None:
                    list_of_entries = history_settings.get('-PSG text list-', [])
                    if values['-INPUT-'] in list_of_entries:
                        list_of_entries.remove(values['-INPUT-'])
                    list_of_entries.insert(0, values['-INPUT-'])
                    history_settings.set('-PSG text list-', list_of_entries)
            break

    window.close()
    del window
    if event in ('Cancel', WIN_CLOSED):
        return None
    else:
        text = values['-INPUT-']
        return text


def popup_get_date(
    start_mon=None,
    start_day=None,
    start_year=None,
    begin_at_sunday_plus=0,
    no_titlebar=True,
    title='Choose Date',
    keep_on_top=True,
    location=(None, None),
    relative_location=(None, None),
    close_when_chosen=False,
    icon=None,
    locale=None,
    month_names=None,
    day_abbreviations=None,
    day_font='TkFixedFont 9',
    mon_year_font='TkFixedFont 10',
    arrow_font='TkFixedFont 7',
    modal=True,
):
    """
    Display a calendar window, get the user's choice, return as a tuple (mon, day, year)

    :param start_mon:            The starting month
    :type start_mon:             (int)
    :param start_day:            The starting day - optional. Set to None or 0 if no date to be chosen at start
    :type start_day:             int | None
    :param start_year:           The starting year
    :type start_year:            (int)
    :param begin_at_sunday_plus: Determines the left-most day in the display. 0=sunday, 1=monday, etc
    :type begin_at_sunday_plus:  (int)
    :param icon:                 Same as Window icon parameter. Can be either a filename or Base64 value. For Windows if filename, it MUST be ICO format. For Linux, must NOT be ICO
    :type icon:                  (str | bytes)
    :param location:             (x,y) location on the screen to place the top left corner of your window. Default is to center on screen
    :type location:              (int, int)
    :param relative_location:    (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:     (int, int)
    :param title:                Title that will be shown on the window
    :type title:                 (str)
    :param close_when_chosen:    If True, the window will close and function return when a day is clicked
    :type close_when_chosen:     (bool)
    :param locale:               locale used to get the day names
    :type locale:                (str)
    :param no_titlebar:          If True no titlebar will be shown
    :type no_titlebar:           (bool)
    :param keep_on_top:          If True the window will remain above all current windows
    :type keep_on_top:           (bool)
    :param month_names:          optional list of month names to use (should be 12 items)
    :type month_names:           List[str]
    :param day_abbreviations:    optional list of abbreviations to display as the day of week
    :type day_abbreviations:     List[str]
    :param day_font:             Font and size to use for the calendar
    :type day_font:              str | tuple
    :param mon_year_font:        Font and size to use for the month and year at the top
    :type mon_year_font:         str | tuple
    :param arrow_font:           Font and size to use for the arrow buttons
    :type arrow_font:            str | tuple
    :param modal:                If True then makes the popup will behave like a Modal window... all other windows are non-operational until this one is closed. Default = True
    :type modal:                 bool
    :return:                     Tuple containing (month, day, year) of chosen date or None if was cancelled
    :rtype:                      None | (int, int, int)
    """

    if month_names is not None and len(month_names) != 12:
        if not SUPPRESS_ERROR_POPUPS:
            popup_error('Incorrect month names list specified. Must have 12 entries.', 'Your list:', month_names)

    if day_abbreviations is not None and len(day_abbreviations) != 7:
        if not SUPPRESS_ERROR_POPUPS:
            popup_error('Incorrect day abbreviation list. Must have 7 entries.', 'Your list:', day_abbreviations)

    now = datetime.datetime.now()
    cur_month, cur_day, cur_year = now.month, now.day, now.year
    cur_month = start_mon or cur_month
    if start_mon is not None:
        cur_day = start_day
    else:
        cur_day = cur_day
    cur_year = start_year or cur_year

    def update_days(window, month, year, begin_at_sunday_plus):
        [window[(week, day)].update('') for day in range(7) for week in range(6)]
        weeks = calendar.monthcalendar(year, month)
        month_days = list(itertools.chain.from_iterable([[0 for _ in range(8 - begin_at_sunday_plus)]] + weeks))
        if month_days[6] == 0:
            month_days = month_days[7:]
            if month_days[6] == 0:
                month_days = month_days[7:]
        for i, day in enumerate(month_days):
            offset = i
            if offset >= 6 * 7:
                break
            window[(offset // 7, offset % 7)].update(str(day) if day else '')

    def make_days_layout():
        days_layout = []
        for week in range(6):
            row = []
            for day in range(7):
                row.append(
                    T(
                        '',
                        size=(4, 1),
                        justification='c',
                        font=day_font,
                        key=(week, day),
                        enable_events=True,
                        pad=(0, 0),
                    )
                )
            days_layout.append(row)
        return days_layout

    # Create table of month names and week day abbreviations

    if day_abbreviations is None or len(day_abbreviations) != 7:
        fwday = calendar.SUNDAY
        try:
            if locale is not None:
                _cal = calendar.LocaleTextCalendar(fwday, locale)
            else:
                _cal = calendar.TextCalendar(fwday)
            day_names = _cal.formatweekheader(3).split()
        except Exception as e:
            print('Exception building day names from locale', locale, e)
            day_names = ('Sun', 'Mon', 'Tue', 'Wed', 'Th', 'Fri', 'Sat')
    else:
        day_names = day_abbreviations

    mon_names = month_names if month_names is not None and len(month_names) == 12 else [calendar.month_name[i] for i in range(1, 13)]
    days_layout = make_days_layout()

    layout = [
        [
            B('◄◄', font=arrow_font, border_width=0, key='-YEAR-DOWN-', pad=((10, 2), 2)),
            B('◄', font=arrow_font, border_width=0, key='-MON-DOWN-', pad=(0, 2)),
            Text(
                f'{mon_names[cur_month - 1]} {cur_year}',
                size=(16, 1),
                justification='c',
                font=mon_year_font,
                key='-MON-YEAR-',
                pad=(0, 2),
            ),
            B('►', font=arrow_font, border_width=0, key='-MON-UP-', pad=(0, 2)),
            B('►►', font=arrow_font, border_width=0, key='-YEAR-UP-', pad=(2, 2)),
        ]
    ]
    layout += [
        [
            Col(
                [
                    [
                        T(
                            day_names[i - (7 - begin_at_sunday_plus) % 7],
                            size=(4, 1),
                            font=day_font,
                            background_color=theme_text_color(),
                            text_color=theme_background_color(),
                            pad=(0, 0),
                        )
                        for i in range(7)
                    ]
                ],
                background_color=theme_text_color(),
                pad=(0, 0),
            )
        ]
    ]
    layout += days_layout
    if not close_when_chosen:
        layout += [[Button('Ok', border_width=0, font='TkFixedFont 8'), Button('Cancel', border_width=0, font='TkFixedFont 8')]]

    window = Window(
        title,
        layout,
        no_titlebar=no_titlebar,
        grab_anywhere=True,
        keep_on_top=keep_on_top,
        font='TkFixedFont 12',
        use_default_focus=False,
        location=location,
        relative_location=relative_location,
        finalize=True,
        icon=icon,
    )

    update_days(window, cur_month, cur_year, begin_at_sunday_plus)

    prev_choice = chosen_mon_day_year = None

    if cur_day:
        chosen_mon_day_year = cur_month, cur_day, cur_year
        for week in range(6):
            for day in range(7):
                if window[(week, day)].DisplayText == str(cur_day):
                    window[(week, day)].update(background_color=theme_text_color(), text_color=theme_background_color())
                    prev_choice = (week, day)
                    break

    if modal or DEFAULT_MODAL_WINDOWS_FORCED:
        window.make_modal()

    while True:  # Event Loop
        event, values = window.read()
        if event in (None, 'Cancel'):
            chosen_mon_day_year = None
            break
        if event == 'Ok':
            break
        if event in ('-MON-UP-', '-MON-DOWN-', '-YEAR-UP-', '-YEAR-DOWN-'):
            cur_month += event == '-MON-UP-'
            cur_month -= event == '-MON-DOWN-'
            cur_year += event == '-YEAR-UP-'
            cur_year -= event == '-YEAR-DOWN-'
            if cur_month > 12:
                cur_month = 1
                cur_year += 1
            elif cur_month < 1:
                cur_month = 12
                cur_year -= 1
            window['-MON-YEAR-'].update(f'{mon_names[cur_month - 1]} {cur_year}')
            update_days(window, cur_month, cur_year, begin_at_sunday_plus)
            if prev_choice:
                window[prev_choice].update(background_color=theme_background_color(), text_color=theme_text_color())
        elif type(event) is tuple:
            if window[event].DisplayText != '':
                chosen_mon_day_year = cur_month, int(window[event].DisplayText), cur_year
                if prev_choice:
                    window[prev_choice].update(background_color=theme_background_color(), text_color=theme_text_color())
                window[event].update(background_color=theme_text_color(), text_color=theme_background_color())
                prev_choice = event
                if close_when_chosen:
                    break
    window.close()
    return chosen_mon_day_year


# --------------------------- PopupAnimated ---------------------------


def popup_animated(
    image_source,
    message=None,
    background_color=None,
    text_color=None,
    font=None,
    no_titlebar=True,
    grab_anywhere=True,
    keep_on_top=True,
    location=(None, None),
    relative_location=(None, None),
    alpha_channel=None,
    time_between_frames=0,
    transparent_color=None,
    title='',
    icon=None,
    no_buffering=False,
):
    """
     Show animation one frame at a time.  This function has its own internal clocking meaning you can call it at any frequency
     and the rate the frames of video is shown remains constant.  Maybe your frames update every 30 ms but your
     event loop is running every 10 ms.  You don't have to worry about delaying, just call it every time through the
     loop.

    :param image_source:        Either a filename or a base64 string. Use None to close the window.
    :type image_source:         str | bytes | None
    :param message:             An optional message to be shown with the animation
    :type message:              (str)
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 str | tuple
    :param no_titlebar:         If True then the titlebar and window frame will not be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True then you can move the window just clicking anywhere on window, hold and drag
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True then Window will remain on top of all other windows currently shownn
    :type keep_on_top:          (bool)
    :param location:            (x,y) location on the screen to place the top left corner of your window. Default is to center on screen
    :type location:             (int, int)
    :param relative_location:   (x,y) location relative to the default location of the window, in pixels. Normally the window centers.  This location is relative to the location the window would be created. Note they can be negative.
    :type relative_location:    (int, int)
    :param alpha_channel:       Window transparency 0 = invisible 1 = completely visible. Values between are see through
    :type alpha_channel:        (float)
    :param time_between_frames: Amount of time in milliseconds between each frame
    :type time_between_frames:  (int)
    :param transparent_color:   This color will be completely see-through in your window. Can even click through
    :type transparent_color:    (str)
    :param title:               Title that will be shown on the window
    :type title:                (str)
    :param icon:                Same as Window icon parameter. Can be either a filename or Base64 byte string. For Windows if filename, it MUST be ICO format. For Linux, must NOT be ICO
    :type icon:                 str | bytes
    :param no_buffering:        If True then no buffering will be used for the GIF. May work better if you have a large animation
    :type no_buffering:         (bool)
    :return:                    True if the window updated OK. False if the window was closed
    :rtype:                     bool
    """
    if image_source is None:
        for image in Window._animated_popup_dict:
            window = Window._animated_popup_dict[image]
            window.close()
        Window._animated_popup_dict = {}
        return

    if image_source not in Window._animated_popup_dict:
        if type(image_source) is bytes or len(image_source) > 300:
            layout = [
                [Image(data=image_source, background_color=background_color, key='-IMAGE-')],
            ]
        else:
            layout = [
                [
                    Image(
                        filename=image_source,
                        background_color=background_color,
                        key='-IMAGE-',
                    )
                ],
            ]
        if message:
            layout.append([Text(message, background_color=background_color, text_color=text_color, font=font)])

        window = Window(
            title,
            layout,
            no_titlebar=no_titlebar,
            grab_anywhere=grab_anywhere,
            keep_on_top=keep_on_top,
            background_color=background_color,
            location=location,
            alpha_channel=alpha_channel,
            element_padding=(0, 0),
            margins=(0, 0),
            transparent_color=transparent_color,
            finalize=True,
            element_justification='c',
            icon=icon,
            relative_location=relative_location,
        )
        Window._animated_popup_dict[image_source] = window
    else:
        window = Window._animated_popup_dict[image_source]
        if no_buffering:
            window['-IMAGE-'].update_animation_no_buffering(image_source, time_between_frames=time_between_frames)
        else:
            window['-IMAGE-'].update_animation(image_source, time_between_frames=time_between_frames)
    event, values = window.read(1)
    if event == WIN_CLOSED:
        return False
    # window.refresh() # call refresh instead of Read to save significant CPU time
    return True


# Popup Notify
def popup_notify(
    *args,
    title='',
    icon=None,
    display_duration_in_ms=None,
    fade_in_duration=None,
    alpha=0.9,
    location=None,
):
    """
    Displays a "notification window", usually in the bottom right corner of your display.  Has an icon, a title, and a message.  It is more like a "toaster" window than the normal popups.

    The window will slowly fade in and out if desired.  Clicking on the window will cause it to move through the end the current "phase". For example, if the window was fading in and it was clicked, then it would immediately stop fading in and instead be fully visible.  It's a way for the user to quickly dismiss the window.

    The return code specifies why the call is returning (e.g. did the user click the message to dismiss it)

    :param title:                  Text to be shown at the top of the window in a larger font
    :type title:                   (str)
    :param message:                Text message that makes up the majority of the window
    :type message:                 (str)
    :param icon:                   A base64 encoded PNG/GIF image or PNG/GIF filename that will be displayed in the window
    :type icon:                    bytes | str
    :param display_duration_in_ms: Number of milliseconds to show the window
    :type display_duration_in_ms:  (int)
    :param fade_in_duration:       Number of milliseconds to fade window in and out
    :type fade_in_duration:        (int)
    :param alpha:                  Alpha channel. 0 - invisible 1 - fully visible
    :type alpha:                   (float)
    :param location:               Location on the screen to display the window
    :type location:                (int, int)
    :return:                       reason for returning
    :rtype:                        (int)
    """
    if icon is None:
        icon = SYSTEM_TRAY_MESSAGE_ICON_INFORMATION
    if display_duration_in_ms is None:
        display_duration_in_ms = SYSTEM_TRAY_MESSAGE_DISPLAY_DURATION_IN_MILLISECONDS
    if fade_in_duration is None:
        fade_in_duration = SYSTEM_TRAY_MESSAGE_FADE_IN_DURATION
    if not args:
        args_to_print = ['']
    else:
        args_to_print = args
    output = ''
    max_line_total, total_lines, local_line_width = 0, 0, SYSTEM_TRAY_MESSAGE_MAX_LINE_LENGTH
    for message in args_to_print:
        # fancy code to check if string and convert if not is not need. Just always convert to string :-)
        # if not isinstance(message, str): message = str(message)
        message = str(message)
        if message.count('\n'):
            message_wrapped = message
        else:
            message_wrapped = textwrap.fill(message, local_line_width)
        message_wrapped_lines = message_wrapped.count('\n') + 1
        longest_line_len = max([len(line) for line in message.split('\n')])
        width_used = min(longest_line_len, local_line_width)
        max_line_total = max(max_line_total, width_used)
        # height = _GetNumLinesNeeded(message, width_used)
        height = message_wrapped_lines
        output += message_wrapped + '\n'
        total_lines += height

    message = output

    # def __init__(self, menu=None, filename=None, data=None, data_base64=None, tooltip=None, metadata=None):
    return SystemTray.notify(
        title=title,
        message=message,
        icon=icon,
        display_duration_in_ms=display_duration_in_ms,
        fade_in_duration=fade_in_duration,
        alpha=alpha,
        location=location,
    )


def popup_menu(window, element, menu_def, title=None, location=(None, None)):
    """
    Makes a "popup menu"
    This type of menu is what you get when a normal menu or a right click menu is torn off
    The settings for the menu are obtained from the window parameter's Window


    :param window:   The window associated with the popup menu. The theme and right click menu settings for this window will be used
    :type window:    Window
    :param element:  An element in your window to associate the menu to. It can be any element
    :type element:   Element
    :param menu_def: A menu definition. This will be the same format as used for Right Click Menus1
    :type  menu_def: List[List[ List[str] | str ]]
    :param title:    The title that will be shown on the torn off menu window. Defaults to window titlr
    :type title:     str
    :param location: The location on the screen to place the window
    :type location:  (int, int) | (None, None)
    """

    element._popup_menu_location = location
    top_menu = tk.Menu(window.TKroot, tearoff=True, tearoffcommand=element._tearoff_menu_callback)
    if window.right_click_menu_background_color not in (COLOR_SYSTEM_DEFAULT, None):
        top_menu.config(bg=window.right_click_menu_background_color)
    if window.right_click_menu_text_color not in (COLOR_SYSTEM_DEFAULT, None):
        top_menu.config(fg=window.right_click_menu_text_color)
    if window.right_click_menu_disabled_text_color not in (COLOR_SYSTEM_DEFAULT, None):
        top_menu.config(disabledforeground=window.right_click_menu_disabled_text_color)
    if window.right_click_menu_font is not None:
        top_menu.config(font=window.right_click_menu_font)
    if window.right_click_menu_selected_colors[0] != COLOR_SYSTEM_DEFAULT:
        top_menu.config(activeforeground=window.right_click_menu_selected_colors[0])
    if window.right_click_menu_selected_colors[1] != COLOR_SYSTEM_DEFAULT:
        top_menu.config(activebackground=window.right_click_menu_selected_colors[1])
    top_menu.config(title=window.Title if title is None else title)
    AddMenuItem(top_menu, menu_def[1], element, right_click_menu=True)
    # element.Widget.bind('<Button-3>', element._RightClickMenuCallback)
    top_menu.invoke(0)


def popup_error_with_traceback(title, *messages, emoji=None):
    """
    Show an error message and as many additoinal lines of messages as you want.
    Will show the same error window as PySimpleGUI uses internally.  Has a button to
    take the user to the line of code you called this popup from.
    If you include the Exception information in your messages, then it will be parsed and additional information
    will be in the window about such as the specific line the error itself occurred on.

    :param title:     The title that will be shown in the popup's titlebar and in the first line of the window
    :type title:      str
    :param *messages: A variable number of lines of messages you wish to show your user
    :type *messages:  Any
    :param emoji:     An optional BASE64 Encoded image to shows in the error window
    :type emoji:      bytes
    """

    # For now, call the function that PySimpleGUI uses internally
    _error_popup_with_traceback(str(title), *messages, emoji=emoji)


#####################################################################
# Animated window while shell command is executed
#####################################################################


def _process_thread(*args):
    global __shell_process__

    # start running the command with arugments
    try:
        __shell_process__ = subprocess.run(args, shell=True, stdout=subprocess.PIPE)
    except Exception:
        print(f'Exception running process args = {args}')
        __shell_process__ = None


def shell_with_animation(
    command,
    args=None,
    image_source=DEFAULT_BASE64_LOADING_GIF,
    message=None,
    background_color=None,
    text_color=None,
    font=None,
    no_titlebar=True,
    grab_anywhere=True,
    keep_on_top=True,
    location=(None, None),
    alpha_channel=None,
    time_between_frames=100,
    transparent_color=None,
):
    """
    Execute a "shell command" (anything capable of being launched using subprocess.run) and
    while the command is running, show an animated popup so that the user knows that a long-running
    command is being executed.  Without this mechanism, the GUI appears locked up.

    :param command:             The command to run
    :type command:              (str)
    :param args:                List of arguments
    :type args:                 List[str]
    :param image_source:        Either a filename or a base64 string.
    :type image_source:         str | bytes
    :param message:             An optional message to be shown with the animation
    :type message:              (str)
    :param background_color:    color of background
    :type background_color:     (str)
    :param text_color:          color of the text
    :type text_color:           (str)
    :param font:                specifies the  font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
    :type font:                 str | tuple
    :param no_titlebar:         If True then the titlebar and window frame will not be shown
    :type no_titlebar:          (bool)
    :param grab_anywhere:       If True then you can move the window just clicking anywhere on window, hold and drag
    :type grab_anywhere:        (bool)
    :param keep_on_top:         If True then Window will remain on top of all other windows currently shownn
    :type keep_on_top:          (bool)
    :param location:            (x,y) location on the screen to place the top left corner of your window. Default is to center on screen
    :type location:             (int, int)
    :param alpha_channel:       Window transparency 0 = invisible 1 = completely visible. Values between are see through
    :type alpha_channel:        (float)
    :param time_between_frames: Amount of time in milliseconds between each frame
    :type time_between_frames:  (int)
    :param transparent_color:   This color will be completely see-through in your window. Can even click through
    :type transparent_color:    (str)
    :return:                    The resulting string output from stdout
    :rtype:                     (str)
    """

    global __shell_process__

    real_args = [command]
    if args is not None:
        for arg in args:
            real_args.append(arg)
        # real_args.append(args)
    thread = threading.Thread(target=_process_thread, args=real_args, daemon=True)
    thread.start()

    # Poll to see if the thread is still running.  If so, then continue showing the animation
    while True:
        popup_animated(
            image_source=image_source,
            message=message,
            time_between_frames=time_between_frames,
            transparent_color=transparent_color,
            text_color=text_color,
            background_color=background_color,
            font=font,
            no_titlebar=no_titlebar,
            grab_anywhere=grab_anywhere,
            keep_on_top=keep_on_top,
            location=location,
            alpha_channel=alpha_channel,
        )
        thread.join(timeout=time_between_frames / 1000)
        if not thread.is_alive():
            break
    popup_animated(None)  # stop running the animation

    output = __shell_process__.__str__().replace('\\r\\n', '\n')  # fix up the output string
    output = output[output.index("stdout=b'") + 9 : -2]
    return output


#######################################################################
#  8888888888
#  888
#  888
#  8888888    888d888 888d888 .d88b.  888d888
#  888        888P"   888P"  d88""88b 888P"
#  888        888     888    888  888 888
#  888        888     888    Y88..88P 888
#  8888888888 888     888     "Y88P"  888
#
#
#
#  888b     d888
#  8888b   d8888
#  88888b.d88888
#  888Y88888P888  .d88b.  .d8888b  .d8888b   8888b.   .d88b.   .d88b.
#  888 Y888P 888 d8P  Y8b 88K      88K          "88b d88P"88b d8P  Y8b
#  888  Y8P  888 88888888 "Y8888b. "Y8888b. .d888888 888  888 88888888
#  888   "   888 Y8b.          X88      X88 888  888 Y88b 888 Y8b.
#  888       888  "Y8888   88888P'  88888P' "Y888888  "Y88888  "Y8888
#                                                         888
#                                                    Y8b d88P
#                                                     "Y88P"
# Code to make messages to help user find errors in their code
#######################################################################


#   .d8888b.           888    888    d8b
#  d88P  Y88b          888    888    Y8P
#  Y88b.               888    888
#   "Y888b.    .d88b.  888888 888888 888 88888b.   .d88b.  .d8888b
#      "Y88b. d8P  Y8b 888    888    888 888 "88b d88P"88b 88K
#        "888 88888888 888    888    888 888  888 888  888 "Y8888b.
#  Y88b  d88P Y8b.     Y88b.  Y88b.  888 888  888 Y88b 888      X88
#   "Y8888P"   "Y8888   "Y888  "Y888 888 888  888  "Y88888  88888P'
#                                                      888
#                                                 Y8b d88P
#                                                  "Y88P"

# Interface to saving / loading user program settings in json format
# This is a new set of APIs supplied by PySimpleGUI that enables users to easily set/save/load individual
# settings.  They are automatically saved to a JSON file. If no file/path is specified then a filename is
# created from the source file filename.


class UserSettings:
    # A reserved settings object for use by the setting functions. It's a way for users
    # to access the user settings without diarectly using the UserSettings class
    _default_for_function_interface = None  # type: UserSettings

    def __init__(
        self,
        filename=None,
        path=None,
        silent_on_error=False,
        autosave=True,
        use_config_file=None,
        convert_bools_and_none=True,
    ):
        """
        User Settings

        :param filename:               The name of the file to use. Can be a full path and filename or just filename
        :type filename:                (str or None)
        :param path:                   The folder that the settings file will be stored in. Do not include the filename.
        :type path:                    (str or None)
        :param silent_on_error:        If True errors will not be reported
        :type silent_on_error:         (bool)
        :param autosave:               If True the settings file is saved after every update
        :type autosave:                (bool)
        :param use_config_file:        If True then the file format will be a config.ini rather than json
        :type use_config_file:         (bool)
        :param convert_bools_and_none: If True then "True", "False", "None" will be converted to the Python values True, False, None when using INI files. Default is TRUE
        :type convert_bools_and_none:  (bool)
        """

        self.path = path
        self.filename = filename
        self.full_filename = None
        self.dict = {}
        self.default_value = None
        self.silent_on_error = silent_on_error
        self.autosave = autosave
        if filename is not None and filename.endswith('.ini') and use_config_file is None:
            warnings.warn(
                '[UserSettings] You have specified a filename with .ini extension but did not set use_config_file. Setting use_config_file for you.',
                UserWarning,
            )
            use_config_file = True
        self.use_config_file = use_config_file
        # self.retain_config_comments = retain_config_comments
        self.convert_bools = convert_bools_and_none
        if use_config_file:
            self.config = configparser.ConfigParser()
            self.config.optionxform = str
            # self.config_dict = {}
            self.section_class_dict = {}  # type: dict[_SectionDict]
        if filename is not None or path is not None:
            self.load(filename=filename, path=path)

    ########################################################################################################
    ## FIRST is the _SectionDict helper class
    ## It is typically not directly accessed, although it is possible to call delete_section, get, set
    ########################################################################################################

    class _SectionDict:
        item_count = 0

        def __init__(self, section_name, section_dict, config, user_settings_parent):  # (str, Dict, configparser.ConfigParser)
            """
            The Section Dictionary.  It holds the values for a section.

            :param section_name:                Name of the section
            :type section_name:                 str
            :param section_dict:                Dictionary of values for the section
            :type section_dict:                 dict
            :param config:                      The configparser object
            :type config:                       configparser.ConfigParser
            :param user_settings_parent:        The parent UserSettings object that hdas this section
            :type user_settings_parent:         UserSettings
            """
            self.section_name = section_name
            self.section_dict = section_dict  # type: Dict
            self.new_section = False
            self.config = config  # type: configparser.ConfigParser
            self.user_settings_parent = user_settings_parent  # type: UserSettings
            UserSettings._SectionDict.item_count += 1

            if self.user_settings_parent.convert_bools:
                for key, value in self.section_dict.items():
                    if value == 'True':
                        value = True
                        self.section_dict[key] = value
                    elif value == 'False':
                        value = False
                        self.section_dict[key] = value
                    elif value == 'None':
                        value = None
                        self.section_dict[key] = value
            # print(f'++++++ making a new SectionDict with name = {section_name}')

        def __repr__(self):
            """
            Converts the settings dictionary into a string for easy display

            :return: the dictionary as a string
            :rtype:  (str)
            """
            return_string = f'{self.section_name}:\n'
            for entry in self.section_dict.keys():
                return_string += f'          {entry} : {self.section_dict[entry]}\n'

            return return_string

        def get(self, key, default=None):
            """
            Returns the value of a specified setting.  If the setting is not found in the settings dictionary, then
            the user specified default value will be returned.  It no default is specified and nothing is found, then
            the "default value" is returned.  This default can be specified in this call, or previously defined
            by calling set_default. If nothing specified now or previously, then None is returned as default.

            :param key:     Key used to lookup the setting in the settings dictionary
            :type key:      (Any)
            :param default: Value to use should the key not be found in the dictionary
            :type default:  (Any)
            :return:        Value of specified settings
            :rtype:         (Any)
            """
            value = self.section_dict.get(key, default)
            if self.user_settings_parent.convert_bools:
                if value == 'True':
                    value = True
                elif value == 'False':
                    value = False
            return value

        def set(self, key, value):
            value = str(value)  # all values must be strings
            if self.new_section:
                self.config.add_section(self.section_name)
                self.new_section = False
            self.config.set(section=self.section_name, option=key, value=value)
            self.section_dict[key] = value
            if self.user_settings_parent.autosave:
                self.user_settings_parent.save()

        def delete_section(self):
            # print(f'** Section Dict deleting section = {self.section_name}')
            self.config.remove_section(section=self.section_name)
            del self.user_settings_parent.section_class_dict[self.section_name]
            if self.user_settings_parent.autosave:
                self.user_settings_parent.save()

        def __getitem__(self, item):
            # print('*** In SectionDict Get ***')
            return self.get(item)

        def __setitem__(self, item, value):
            """
            Enables setting a setting by using [ ] notation like a dictionary.
            Your code will have this kind of design pattern:
            settings = sg.UserSettings()
            settings[item] = value

            :param item:  The key for the setting to change. Needs to be a hashable type. Basically anything but a list
            :type item:   Any
            :param value: The value to set the setting to
            :type value:  Any
            """
            # print(f'*** In SectionDict SET *** item = {item} value = {value}')
            self.set(item, value)
            self.section_dict[item] = value

        def __delitem__(self, item):
            """
            Delete an individual user setting.  This is the same as calling delete_entry.  The syntax
            for deleting the item using this manner is:
                del settings['entry']
            :param item: The key for the setting to delete
            :type item:  Any
            """
            # print(f'** In SectionDict delete! section name = {self.section_name} item = {item} ')
            self.config.remove_option(section=self.section_name, option=item)
            try:
                del self.section_dict[item]
            except Exception:
                pass
            if self.user_settings_parent.autosave:
                self.user_settings_parent.save()

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

    def __repr__(self):
        """
        Converts the settings dictionary into a string for easy display

        :return: the dictionary as a string
        :rtype:  (str)
        """
        if not self.use_config_file:
            return pprint.pformat(self.dict)
        else:
            # rvalue = '-------------------- Settings ----------------------\n'
            rvalue = ''
            for name, section in self.section_class_dict.items():
                rvalue += str(section)

            # rvalue += '\n-------------------- Settings End----------------------\n'
            rvalue += '\n'
            return rvalue

    def set_default_value(self, default):
        """
        Set the value that will be returned if a requested setting is not found

        :param default: value to be returned if a setting is not found in the settings dictionary
        :type default:  Any
        """
        self.default_value = default

    def _compute_filename(self, filename=None, path=None):
        """
        Creates the full filename given the path or the filename or both.

        :param filename: The name of the file to use. Can be a full path and filename or just filename
        :type filename:  (str or None)
        :param path:     The folder that the settings file will be stored in. Do not include the filename.
        :type path:      (str or None)
        :return:         Tuple with (full filename, path, filename)
        :rtype:          Tuple[str, str, str]
        """
        if filename is not None:
            dirname_from_filename = os.path.dirname(filename)  # see if a path was provided as part of filename
            if dirname_from_filename:
                path = dirname_from_filename
                filename = os.path.basename(filename)
        elif self.filename is not None:
            filename = self.filename
        else:
            if not self.use_config_file:
                filename = os.path.splitext(os.path.basename(sys.modules['__main__'].__file__))[0] + '.json'
            else:
                filename = os.path.splitext(os.path.basename(sys.modules['__main__'].__file__))[0] + '.ini'

        if path is None:
            if self.path is not None:
                # path = self.path
                path = os.path.expanduser(self.path)  # expand user provided path in case it has user ~ in it. Don't think it'll hurt
            elif DEFAULT_USER_SETTINGS_PATH is not None:  # if user set the path manually system-wide using set options
                path = os.path.expanduser(DEFAULT_USER_SETTINGS_PATH)
            elif running_trinket():
                path = os.path.expanduser(DEFAULT_USER_SETTINGS_TRINKET_PATH)
            elif running_replit():
                path = os.path.expanduser(DEFAULT_USER_SETTINGS_REPLIT_PATH)
            elif running_windows():
                path = os.path.expanduser(DEFAULT_USER_SETTINGS_WIN_PATH)
            elif running_linux():
                path = os.path.expanduser(DEFAULT_USER_SETTINGS_LINUX_PATH)
            elif running_mac():
                path = os.path.expanduser(DEFAULT_USER_SETTINGS_MAC_PATH)
            else:
                path = '.'

        full_filename = os.path.join(path, filename)
        return (full_filename, path, filename)

    def set_location(self, filename=None, path=None):
        """
        Sets the location of the settings file

        :param filename: The name of the file to use. Can be a full path and filename or just filename
        :type filename:  (str or None)
        :param path:     The folder that the settings file will be stored in. Do not include the filename.
        :type path:      (str or None)
        """
        cfull_filename, cpath, cfilename = self._compute_filename(filename=filename, path=path)

        self.filename = cfilename
        self.path = cpath
        self.full_filename = cfull_filename

    def get_filename(self, filename=None, path=None):
        """
        Sets the filename and path for your settings file.  Either paramter can be optional.

        If you don't choose a path, one is provided for you that is OS specific
        Windows path default = users/name/AppData/Local/PySimpleGUI/settings.

        If you don't choose a filename, your application's filename + '.json' will be used.

        Normally the filename and path are split in the user_settings calls. However for this call they
        can be combined so that the filename contains both the path and filename.

        :param filename: The name of the file to use. Can be a full path and filename or just filename
        :type filename:  (str or None)
        :param path:     The folder that the settings file will be stored in. Do not include the filename.
        :type path:      (str or None)
        :return:         The full pathname of the settings file that has both the path and filename combined.
        :rtype:          (str)
        """
        if filename is not None or path is not None or (filename is None and path is None and self.full_filename is None):
            self.set_location(filename=filename, path=path)
            self.read()
        return self.full_filename

    def save(self, filename=None, path=None):
        """
        Saves the current settings dictionary.  If a filename or path is specified in the call, then it will override any
        previously specitfied filename to create a new settings file.  The settings dictionary is then saved to the newly defined file.

        :param filename: The fFilename to save to. Can specify a path or just the filename. If no filename specified, then the caller's filename will be used.
        :type filename:  (str or None)
        :param path:     The (optional) path to use to save the file.
        :type path:      (str or None)
        :return:         The full path and filename used to save the settings
        :rtype:          (str)
        """
        if filename is not None or path is not None:
            self.set_location(filename=filename, path=path)
        try:
            if not os.path.exists(self.path):
                os.makedirs(self.path)
            with open(self.full_filename, 'w') as f:
                if not self.use_config_file:
                    json.dump(self.dict, f, indent=4)
                else:
                    self.config.write(f)
        except Exception as e:
            if not self.silent_on_error:
                _error_popup_with_traceback(
                    'UserSettings.save error',
                    '*** UserSettings.save()  Error saving settings to file:***\n',
                    self.full_filename,
                    e,
                )

        return self.full_filename

    def load(self, filename=None, path=None):
        """
        Specifies the path and filename to use for the settings and reads the contents of the file.
        The filename can be a full filename including a path, or the path can be specified separately.
        If  no filename is specified, then the caller's filename will be used with the extension ".json"

        :param filename: Filename to load settings from (and save to in the future)
        :type filename:  (str or None)
        :param path:     Path to the file. Defaults to a specific folder depending on the operating system
        :type path:      (str or None)
        :return:         The settings dictionary (i.e. all settings)
        :rtype:          (dict)
        """
        if filename is not None or path is not None or self.full_filename is None:
            self.set_location(filename, path)
        self.read()
        return self.dict

    def delete_file(self, filename=None, path=None, report_error=False):
        """
        Deltes the filename and path for your settings file.  Either paramter can be optional.
        If you don't choose a path, one is provided for you that is OS specific
        Windows path default = users/name/AppData/Local/PySimpleGUI/settings.
        If you don't choose a filename, your application's filename + '.json' will be used
        Also sets your current dictionary to a blank one.

        :param filename:     The name of the file to use. Can be a full path and filename or just filename
        :type filename:      (str or None)
        :param path:         The folder that the settings file will be stored in. Do not include the filename.
        :type path:          (str or None)
        :param report_error: Determines if an error should be shown if a delete error happen (i.e. file isn't present)
        :type report_error:  (bool)
        """

        if filename is not None or path is not None or (filename is None and path is None):
            self.set_location(filename=filename, path=path)
        try:
            os.remove(self.full_filename)
        except Exception as e:
            if report_error:
                _error_popup_with_traceback('UserSettings delete_file warning ***', 'Exception trying to perform os.remove', e)
        self.dict = {}

    def write_new_dictionary(self, settings_dict):
        """
        Writes a specified dictionary to the currently defined settings filename.

        :param settings_dict: The dictionary to be written to the currently defined settings file
        :type settings_dict:  (dict)
        """
        if self.full_filename is None:
            self.set_location()
        self.dict = settings_dict
        self.save()

    def read(self):
        """
        Reads settings file and returns the dictionary.
        If you have anything changed in an existing settings dictionary, you will lose your changes.
        :return: settings dictionary
        :rtype:  (dict)
        """
        if self.full_filename is None:
            return {}
        try:
            if os.path.exists(self.full_filename):
                with open(self.full_filename) as f:
                    if not self.use_config_file:  # if using json
                        self.dict = json.load(f)
                    else:  # if using a config file
                        self.config.read_file(f)
                        # Make a dictionary of SectionDict classses. Keys are the config.sections().
                        self.section_class_dict = {}
                        for section in self.config.sections():
                            section_dict = dict(self.config[section])
                            self.section_class_dict[section] = self._SectionDict(section, section_dict, self.config, self)

                        self.dict = self.section_class_dict
                        self.config_sections = self.config.sections()
                        # self.config_dict = {section_name : dict(self.config[section_name]) for section_name in self.config.sections()}
                    # if self.retain_config_comments:
                    #     self.config_file_contents = f.readlines()
        except Exception as e:
            if not self.silent_on_error:
                _error_popup_with_traceback('User Settings read warning', 'Error reading settings from file', self.full_filename, e)
                # print('*** UserSettings.read - Error reading settings from file: ***\n', self.full_filename, e)
                # print(_create_error_message())

        return self.dict

    def exists(self, filename=None, path=None):
        """
        Check if a particular settings file exists.  Returns True if file exists

        :param filename: The name of the file to use. Can be a full path and filename or just filename
        :type filename:  (str or None)
        :param path:     The folder that the settings file will be stored in. Do not include the filename.
        :type path:      (str or None)
        """
        cfull_filename, cpath, cfilename = self._compute_filename(filename=filename, path=path)
        if os.path.exists(cfull_filename):
            return True
        return False

    def delete_entry(self, key, section=None, silent_on_error=None):
        """
        Deletes an individual entry.  If no filename has been specified up to this point,
        then a default filename will be used.
        After value has been deleted, the settings file is written to disk.

        :param key: Setting to be deleted. Can be any valid dictionary key type (i.e. must be hashable)
        :type key:  (Any)
        :param silent_on_error: Determines if error should be shown. This parameter overrides the silent on error setting for the object.
        :type silent_on_error:  (bool)
        """
        if self.full_filename is None:
            self.set_location()
            self.read()
        if not self.use_config_file:  # Is using JSON file
            if key in self.dict:
                del self.dict[key]
                if self.autosave:
                    self.save()
            else:
                if silent_on_error is False or (silent_on_error is not True and not self.silent_on_error):
                    _error_popup_with_traceback('User Settings delete_entry Warning - key', key, ' not found in settings')

        else:
            if section is not None:
                del self.get(section)[key]

    def delete_section(self, section):
        """
        Deletes a section with the name provided in the section parameter.  Your INI file will be saved afterwards if auto-save enabled (default is ON)
        :param section:     Name of the section to delete
        :type section:      str
        """
        if not self.use_config_file:
            return

        section_dict = self.section_class_dict.get(section, None)
        section_dict.delete_section()
        del self.section_class_dict[section]
        if self.autosave:
            self.save()

    def set(self, key, value):
        """
        Sets an individual setting to the specified value.  If no filename has been specified up to this point,
        then a default filename will be used.
        After value has been modified, the settings file is written to disk.
        Note that this call is not value for a config file normally. If it is, then the key is assumed to be the
            Section key and the value written will be the default value.
        :param key:      Setting to be saved. Can be any valid dictionary key type
        :type key:       (Any)
        :param value:    Value to save as the setting's value. Can be anything
        :type value:     (Any)
        :return:         value that key was set to
        :rtype:          (Any)
        """

        if self.full_filename is None:
            self.set_location()
        # if not autosaving, then don't read the file or else will lose changes
        if not self.use_config_file:
            if self.autosave or self.dict == {}:
                self.read()
            self.dict[key] = value
        else:
            self.section_class_dict[key].set(value, self.default_value)

        if self.autosave:
            self.save()
        return value

    def get(self, key, default=None):
        """
        Returns the value of a specified setting.  If the setting is not found in the settings dictionary, then
        the user specified default value will be returned.  It no default is specified and nothing is found, then
        the "default value" is returned.  This default can be specified in this call, or previously defined
        by calling set_default. If nothing specified now or previously, then None is returned as default.

        :param key:     Key used to lookup the setting in the settings dictionary
        :type key:      (Any)
        :param default: Value to use should the key not be found in the dictionary
        :type default:  (Any)
        :return:        Value of specified settings
        :rtype:         (Any)
        """
        if self.default_value is not None:
            default = self.default_value

        if self.full_filename is None:
            self.set_location()
            if self.autosave or self.dict == {}:
                self.read()
        if not self.use_config_file:
            value = self.dict.get(key, default)
        else:
            value = self.section_class_dict.get(key, None)
            if key not in list(self.section_class_dict.keys()):
                self.section_class_dict[key] = self._SectionDict(key, {}, self.config, self)
                value = self.section_class_dict[key]
                value.new_section = True
        return value

    def get_dict(self):
        """
        Returns the current settings dictionary.  If you've not setup the filename for the
        settings, a default one will be used and then read.

        Note that you can display the dictionary in text format by printing the object itself.

        :return: The current settings dictionary
        :rtype:  Dict
        """
        if self.full_filename is None:
            self.set_location()
            if self.autosave or self.dict == {}:
                self.read()
                self.save()
        return self.dict

    def __setitem__(self, item, value):
        """
        Enables setting a setting by using [ ] notation like a dictionary.
        Your code will have this kind of design pattern:
        settings = sg.UserSettings()
        settings[item] = value

        :param item:  The key for the setting to change. Needs to be a hashable type. Basically anything but a list
        :type item:   Any
        :param value: The value to set the setting to
        :type value:  Any
        """
        return self.set(item, value)

    def __getitem__(self, item):
        """
        Enables accessing a setting using [ ] notation like a dictionary.
        If the entry does not exist, then the default value will be returned.  This default
        value is None unless user sets by calling UserSettings.set_default_value(default_value)

        :param item: The key for the setting to change. Needs to be a hashable type. Basically anything but a list
        :type item:  Any
        :return:     The setting value
        :rtype:      Any
        """
        return self.get(item, self.default_value)

    def __delitem__(self, item):
        """
        Delete an individual user setting.  This is the same as calling delete_entry.  The syntax
        for deleting the item using this manner is:
            del settings['entry']
        :param item: The key for the setting to delete
        :type item:  Any
        """
        if self.use_config_file:
            return self.get(item)
        else:
            self.delete_entry(key=item)


# Create a singleton for the settings information so that the settings functions can be used
if UserSettings._default_for_function_interface is None:
    UserSettings._default_for_function_interface = UserSettings()


def user_settings_filename(filename=None, path=None):
    """
    Sets the filename and path for your settings file.  Either paramter can be optional.

    If you don't choose a path, one is provided for you that is OS specific
    Windows path default = users/name/AppData/Local/PySimpleGUI/settings.

    If you don't choose a filename, your application's filename + '.json' will be used.

    Normally the filename and path are split in the user_settings calls. However for this call they
    can be combined so that the filename contains both the path and filename.

    :param filename: The name of the file to use. Can be a full path and filename or just filename
    :type filename:  (str)
    :param path:     The folder that the settings file will be stored in. Do not include the filename.
    :type path:      (str)
    :return:         The full pathname of the settings file that has both the path and filename combined.
    :rtype:          (str)
    """
    settings = UserSettings._default_for_function_interface
    return settings.get_filename(filename, path)


def user_settings_delete_filename(filename=None, path=None, report_error=False):
    """
    Deltes the filename and path for your settings file.  Either paramter can be optional.
    If you don't choose a path, one is provided for you that is OS specific
    Windows path default = users/name/AppData/Local/PySimpleGUI/settings.
    If you don't choose a filename, your application's filename + '.json' will be used
    Also sets your current dictionary to a blank one.

    :param filename: The name of the file to use. Can be a full path and filename or just filename
    :type filename:  (str)
    :param path:     The folder that the settings file will be stored in. Do not include the filename.
    :type path:      (str)
    """
    settings = UserSettings._default_for_function_interface
    settings.delete_file(filename, path, report_error=report_error)


def user_settings_set_entry(key, value):
    """
    Sets an individual setting to the specified value.  If no filename has been specified up to this point,
    then a default filename will be used.
    After value has been modified, the settings file is written to disk.

    :param key:   Setting to be saved. Can be any valid dictionary key type
    :type key:    (Any)
    :param value: Value to save as the setting's value. Can be anything
    :type value:  (Any)
    """
    settings = UserSettings._default_for_function_interface
    settings.set(key, value)


def user_settings_delete_entry(key, silent_on_error=None):
    """
    Deletes an individual entry.  If no filename has been specified up to this point,
    then a default filename will be used.
    After value has been deleted, the settings file is written to disk.

    :param key: Setting to be saved. Can be any valid dictionary key type (hashable)
    :type key:  (Any)
    :param silent_on_error: Determines if an error popup should be shown if an error occurs. Overrides the silent onf effort setting from initialization
    :type silent_on_error:  (bool)
    """
    settings = UserSettings._default_for_function_interface
    settings.delete_entry(key, silent_on_error=silent_on_error)


def user_settings_get_entry(key, default=None):
    """
    Returns the value of a specified setting.  If the setting is not found in the settings dictionary, then
    the user specified default value will be returned.  It no default is specified and nothing is found, then
    None is returned.  If the key isn't in the dictionary, then it will be added and the settings file saved.
    If no filename has been specified up to this point, then a default filename will be assigned and used.
    The settings are SAVED prior to returning.

    :param key:     Key used to lookup the setting in the settings dictionary
    :type key:      (Any)
    :param default: Value to use should the key not be found in the dictionary
    :type default:  (Any)
    :return:        Value of specified settings
    :rtype:         (Any)
    """
    settings = UserSettings._default_for_function_interface
    return settings.get(key, default)


def user_settings_save(filename=None, path=None):
    """
    Saves the current settings dictionary.  If a filename or path is specified in the call, then it will override any
    previously specitfied filename to create a new settings file.  The settings dictionary is then saved to the newly defined file.

    :param filename: The fFilename to save to. Can specify a path or just the filename. If no filename specified, then the caller's filename will be used.
    :type filename:  (str)
    :param path:     The (optional) path to use to save the file.
    :type path:      (str)
    :return:         The full path and filename used to save the settings
    :rtype:          (str)
    """
    settings = UserSettings._default_for_function_interface
    return settings.save(filename, path)


def user_settings_load(filename=None, path=None):
    """
    Specifies the path and filename to use for the settings and reads the contents of the file.
    The filename can be a full filename including a path, or the path can be specified separately.
    If  no filename is specified, then the caller's filename will be used with the extension ".json"

    :param filename: Filename to load settings from (and save to in the future)
    :type filename:  (str)
    :param path:     Path to the file. Defaults to a specific folder depending on the operating system
    :type path:      (str)
    :return:         The settings dictionary (i.e. all settings)
    :rtype:          (dict)
    """
    settings = UserSettings._default_for_function_interface
    return settings.load(filename, path)


def user_settings_file_exists(filename=None, path=None):
    """
    Determines if a settings file exists.  If so a boolean True is returned.
    If either a filename or a path is not included, then the appropriate default
    will be used.

    :param filename: Filename to check
    :type filename:  (str)
    :param path:     Path to the file. Defaults to a specific folder depending on the operating system
    :type path:      (str)
    :return:         True if the file exists
    :rtype:          (bool)
    """
    settings = UserSettings._default_for_function_interface
    return settings.exists(filename=filename, path=path)


def user_settings_write_new_dictionary(settings_dict):
    """
    Writes a specified dictionary to the currently defined settings filename.

    :param settings_dict: The dictionary to be written to the currently defined settings file
    :type settings_dict:  (dict)
    """
    settings = UserSettings._default_for_function_interface
    settings.write_new_dictionary(settings_dict)


def user_settings_silent_on_error(silent_on_error=False):
    """
    Used to control the display of error messages.  By default, error messages are displayed to stdout.

    :param silent_on_error: If True then all error messages are silenced (not displayed on the console)
    :type silent_on_error:  (bool)
    """
    settings = UserSettings._default_for_function_interface
    settings.silent_on_error = silent_on_error


def user_settings():
    """
    Returns the current settings dictionary.  If you've not setup the filename for the
    settings, a default one will be used and then read.
    :return:            The current settings dictionary as a dictionary or a nicely formatted string representing it
    :rtype:             (dict or str)
    """
    settings = UserSettings._default_for_function_interface
    return settings.get_dict()


def user_settings_object():
    """
    Returns the object that is used for the function version of this API.
    With this object you can use the object interface, print it out in a nice format, etc.

    :return:    The UserSettings obect used for the function level interface
    :rtype:     (UserSettings)
    """
    return UserSettings._default_for_function_interface


'''
'########:'##::::'##:'########::'######::'##::::'##:'########:'########:
 ##.....::. ##::'##:: ##.....::'##... ##: ##:::: ##:... ##..:: ##.....::
 ##::::::::. ##'##::: ##::::::: ##:::..:: ##:::: ##:::: ##:::: ##:::::::
 ######:::::. ###:::: ######::: ##::::::: ##:::: ##:::: ##:::: ######:::
 ##...:::::: ## ##::: ##...:::: ##::::::: ##:::: ##:::: ##:::: ##...::::
 ##:::::::: ##:. ##:: ##::::::: ##::: ##: ##:::: ##:::: ##:::: ##:::::::
 ########: ##:::. ##: ########:. ######::. #######::::: ##:::: ########:
........::..:::::..::........:::......::::.......::::::..:::::........::
:::'###::::'########::'####::'######::
::'## ##::: ##.... ##:. ##::'##... ##:
:'##:. ##:: ##:::: ##:: ##:: ##:::..::
'##:::. ##: ########::: ##::. ######::
 #########: ##.....:::: ##:::..... ##:
 ##.... ##: ##::::::::: ##::'##::: ##:
 ##:::: ##: ##::::::::'####:. ######::
..:::::..::..:::::::::....:::......:::



These are the functions used to implement the subprocess APIs (Exec APIs) of PySimpleGUI

'''


def execute_command_subprocess(command, *args, wait=False, cwd=None, pipe_output=False, merge_stderr_with_stdout=True, stdin=None):
    """
    Runs the specified command as a subprocess.
    By default the call is non-blocking.
    The function will immediately return without waiting for the process to complete running. You can use the returned Popen object to communicate with the subprocess and get the results.
    Returns a subprocess Popen object.

    :param command:                  The command/file to execute. What you would type at a console to run a program or shell command.
    :type command:                   (str)
    :param *args:                    Variable number of arguments that are passed to the program being started as command line parms
    :type *args:                     (Any)
    :param wait:                     If True then wait for the subprocess to finish
    :type wait:                      (bool)
    :param cwd:                      Working directory to use when executing the subprocess
    :type cwd:                       (str))
    :param pipe_output:              If True then output from the subprocess will be piped. You MUST empty the pipe by calling execute_get_results or your subprocess will block until no longer full
    :type pipe_output:               (bool)
    :param merge_stderr_with_stdout: If True then output from the subprocess stderr will be merged with stdout. The result is ALL output will be on stdout.
    :type merge_stderr_with_stdout:  (bool)
    :param stdin:                    Value passed to the Popen call. Defaults to subprocess.DEVNULL so that the pyinstaller created executable work correctly
    :type stdin:                     (bool)
    :return:                         Popen object
    :rtype:                          (subprocess.Popen)
    """
    if stdin is None:
        stdin = subprocess.DEVNULL
    try:
        if args is not None:
            expanded_args = ' '.join(args)
            # print('executing subprocess command:',command, 'args:',expanded_args)
            if command[0] != '"' and ' ' in command:
                command = '"' + command + '"'
            # print('calling popen with:', command +' '+ expanded_args)
            # sp = subprocess.Popen(command +' '+ expanded_args, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cwd)
            if pipe_output:
                if merge_stderr_with_stdout:
                    sp = subprocess.Popen(
                        command + ' ' + expanded_args,
                        shell=True,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT,
                        cwd=cwd,
                        stdin=stdin,
                    )
                else:
                    sp = subprocess.Popen(
                        command + ' ' + expanded_args,
                        shell=True,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        cwd=cwd,
                        stdin=stdin,
                    )
            else:
                sp = subprocess.Popen(
                    command + ' ' + expanded_args,
                    shell=True,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                    cwd=cwd,
                    stdin=stdin,
                )
        else:
            sp = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, stdin=stdin)
        if wait:
            out, err = sp.communicate()
            if out:
                print(out.decode('utf-8'))
            if err:
                print(err.decode('utf-8'))
    except Exception as e:
        warnings.warn(f'Error in execute_command_subprocess {e}', UserWarning)
        _error_popup_with_traceback(
            'Error in execute_command_subprocess',
            e,
            f'command={command}',
            f'args={args}',
            f'cwd={cwd}',
        )
        sp = None
    return sp


def execute_py_file(pyfile, parms=None, cwd=None, interpreter_command=None, wait=False, pipe_output=False, merge_stderr_with_stdout=True):
    """
    Executes a Python file.
    The interpreter to use is chosen based on this priority order:
        1. interpreter_command paramter
        2. global setting "-python command-"
        3. the interpreter running running PySimpleGUI
    :param pyfile:                   the file to run
    :type pyfile:                    (str)
    :param parms:                    parameters to pass on the command line
    :type parms:                     (str)
    :param cwd:                      the working directory to use
    :type cwd:                       (str)
    :param interpreter_command:      the command used to invoke the Python interpreter
    :type interpreter_command:       (str)
    :param wait:                     the working directory to use
    :type wait:                      (bool)
    :param pipe_output:              If True then output from the subprocess will be piped. You MUST empty the pipe by calling execute_get_results or your subprocess will block until no longer full
    :type pipe_output:               (bool)
    :param merge_stderr_with_stdout: If True then output from the subprocess stderr will be merged with stdout. The result is ALL output will be on stdout.
    :type merge_stderr_with_stdout:  (bool)
    :return:                         Popen object
    :rtype:                          (subprocess.Popen) | None
    """

    if cwd is None:
        # if the specific file is not found (not an absolute path) then assume it's relative to '.'
        if not os.path.exists(pyfile):
            cwd = '.'

    if pyfile[0] != '"' and ' ' in pyfile:
        pyfile = '"' + pyfile + '"'
    if interpreter_command is not None:
        python_program = interpreter_command
    else:
        # use the version CURRENTLY RUNNING if nothing is specified. Previously used the one from the settings file
        # ^ hmmm... that's not the code is doing now... it's getting the one from the settings file first
        pysimplegui_user_settings.load()  # Refresh the settings just in case they've changed via another program
        python_program = pysimplegui_user_settings.get('-python command-', '')
        if python_program == '':  # if no interpreter set in the settings, then use the current one
            python_program = sys.executable
            # python_program = 'python' if running_windows() else 'python3'
    if parms is not None and python_program:
        sp = execute_command_subprocess(
            python_program,
            pyfile,
            parms,
            wait=wait,
            cwd=cwd,
            pipe_output=pipe_output,
            merge_stderr_with_stdout=merge_stderr_with_stdout,
        )
    elif python_program:
        sp = execute_command_subprocess(
            python_program,
            pyfile,
            wait=wait,
            cwd=cwd,
            pipe_output=pipe_output,
            merge_stderr_with_stdout=merge_stderr_with_stdout,
        )
    else:
        print('execute_py_file - No interpreter has been configured')
        sp = None
    return sp


def execute_py_get_interpreter():
    """
    Returns Python Interpreter from the system settings. If none found in the settings file
    then the currently running interpreter is returned.

    :return: Full path to python interpreter (uses settings file or sys.executable)
    :rtype:  (str)
    """
    pysimplegui_user_settings.load()  # Refresh the settings just in case they've changed via another program
    interpreter = pysimplegui_user_settings.get('-python command-', '')
    if interpreter == '':
        interpreter = sys.executable
    return interpreter


def execute_py_get_running_interpreter():
    """
    Returns the command that is currently running.

    :return: Full path to python interpreter (uses sys.executable)
    :rtype:  (str)
    """
    return sys.executable


def execute_editor(file_to_edit, line_number=None):
    """
    Runs the editor that was configured in the global settings and opens the file to a specific line number.
    Two global settings keys are used.
    '-editor program-' the command line used to startup your editor. It's set
        in the global settings window or by directly manipulating the PySimpleGUI settings object
    '-editor format string-' a string containing 3 "tokens" that describes the command that is executed
            <editor> <file> <line>
    :param file_to_edit: the full path to the file to edit
    :type file_to_edit:  (str)
    :param line_number:  optional line number to place the cursor
    :type line_number:   (int)
    :return:             Popen object
    :rtype:              (subprocess.Popen) | None
    """
    if file_to_edit is not None and len(file_to_edit) != 0 and file_to_edit[0] not in ('\"', "\'") and ' ' in file_to_edit:
        file_to_edit = '"' + file_to_edit + '"'
    pysimplegui_user_settings.load()  # Refresh the settings just in case they've changed via another program
    editor_program = pysimplegui_user_settings.get('-editor program-', None)
    if editor_program is not None:
        format_string = pysimplegui_user_settings.get('-editor format string-', None)
        # if no format string, then just launch the editor with the filename
        if not format_string or line_number is None:
            sp = execute_command_subprocess(editor_program, file_to_edit)
        else:
            command = _create_full_editor_command(file_to_edit, line_number, format_string)
            # print('final command line = ', command)
            sp = execute_command_subprocess(editor_program, command)
    else:
        print('No editor has been configured in the global settings')
        sp = None
    return sp


def execute_get_results(subprocess_id, timeout=None):
    """
    Get the text results of a previously executed execute call
    Returns a tuple of the strings (stdout, stderr)
    :param subprocess_id: a Popen subprocess ID returned from a previous execute call
    :type subprocess_id:  (subprocess.Popen)
    :param timeout:       Time in fractions of a second to wait. Returns '','' if timeout. Default of None means wait forever
    :type timeout:        (None | float)
    :returns:             Tuple with 2 strings (stdout, stderr)
    :rtype:               (str | None , str | None)
    """

    out_decoded = err_decoded = None
    if subprocess_id is not None:
        try:
            out, err = subprocess_id.communicate(timeout=timeout)
            if out:
                out_decoded = out.decode('utf-8')
            if err:
                err_decoded = err.decode('utf-8')
        except ValueError:
            # will get an error if stdout and stderr are combined and attempt to read stderr
            # so ignore the error that would be generated
            pass
        except subprocess.TimeoutExpired:
            # a Timeout error is not actually an error that needs to be reported
            pass
        except Exception as e:
            popup_error('Error in execute_get_results', e)
    return out_decoded, err_decoded


def execute_subprocess_still_running(subprocess_id):
    """
    Returns True is the subprocess ID provided is for a process that is still running

    :param subprocess_id: ID previously returned from Exec API calls that indicate this value is returned
    :type subprocess_id:  (subprocess.Popen)
    :return:              True if the subproces is running
    :rtype:               bool
    """
    if subprocess_id.poll() == 0:
        return False
    return True


def execute_file_explorer(folder_to_open=''):
    """
    The global settings has a setting called -   "-explorer program-"
    It defines the program to run when this function is called.
    The optional folder paramter specified which path should be opened.

    :param folder_to_open: The path to open in the explorer program
    :type folder_to_open:  str
    :return:               Popen object
    :rtype:                (subprocess.Popen) | None
    """
    pysimplegui_user_settings.load()  # Refresh the settings just in case they've changed via another program
    explorer_program = pysimplegui_user_settings.get('-explorer program-', None)
    if explorer_program is not None:
        sp = execute_command_subprocess(explorer_program, folder_to_open)
    else:
        print('No file explorer has been configured in the global settings')
        sp = None
    return sp


def execute_find_callers_filename():
    """
    Returns the first filename found in a traceback that is not the name of this file (__file__)
    Used internally with the debugger for example.

    :return: filename of the caller, assumed to be the first non PySimpleGUI file
    :rtype:  str
    """
    try:  # lots can go wrong so wrapping the entire thing
        trace_details = traceback.format_stack()
        file_info_pysimplegui, error_message = None, ''
        for line in reversed(trace_details):
            if __file__ not in line:
                file_info_pysimplegui = line.split(',')[0]
                error_message = line
                break
        if file_info_pysimplegui is None:
            return ''
        error_parts = None
        if error_message != '':
            error_parts = error_message.split(', ')
            if len(error_parts) < 4:
                error_message = error_parts[0] + '\n' + error_parts[1] + '\n' + ''.join(error_parts[2:])
        if error_parts is None:
            print('*** Error popup attempted but unable to parse error details ***')
            print(trace_details)
            return ''
        filename = error_parts[0][error_parts[0].index('File ') + 5 :]
        return filename
    except:
        return ''


def _create_full_editor_command(file_to_edit, line_number, edit_format_string):
    """
    The global settings has a setting called -   "-editor format string-"
    It uses 3 "tokens" to describe how to invoke the editor in a way that starts at a specific line #
    <editor> <file> <line>

    :param file_to_edit:
    :type file_to_edit:        str
    :param edit_format_string:
    :type edit_format_string:  str
    :return:
    :rtype:
    """

    command = edit_format_string
    command = command.replace('<editor>', '')
    command = command.replace('<file>', file_to_edit)
    command = command.replace('<line>', str(line_number) if line_number is not None else '')
    return command


def execute_get_editor():
    """
    Get the path to the editor based on user settings or on PySimpleGUI's global settings

    :return: Path to the editor
    :rtype:  str
    """
    try:  # in case running with old version of PySimpleGUI that doesn't have a global PSG settings path
        global_editor = pysimplegui_user_settings.get('-editor program-')
    except:
        global_editor = ''

    return user_settings_get_entry('-editor program-', global_editor)


'''
'##::::'##::::'###:::::'######::::::'######::'########::'########::'######::'####:'########:'####::'######::
 ###::'###:::'## ##:::'##... ##::::'##... ##: ##.... ##: ##.....::'##... ##:. ##:: ##.....::. ##::'##... ##:
 ####'####::'##:. ##:: ##:::..::::: ##:::..:: ##:::: ##: ##::::::: ##:::..::: ##:: ##:::::::: ##:: ##:::..::
 ## ### ##:'##:::. ##: ##::::::::::. ######:: ########:: ######::: ##:::::::: ##:: ######:::: ##:: ##:::::::
 ##. #: ##: #########: ##:::::::::::..... ##: ##.....::: ##...:::: ##:::::::: ##:: ##...::::: ##:: ##:::::::
 ##:.:: ##: ##.... ##: ##::: ##::::'##::: ##: ##:::::::: ##::::::: ##::: ##:: ##:: ##:::::::: ##:: ##::: ##:
 ##:::: ##: ##:::: ##:. ######:::::. ######:: ##:::::::: ########:. ######::'####: ##:::::::'####:. ######::
..:::::..::..:::::..:::......:::::::......:::..:::::::::........:::......:::....::..::::::::....:::......:::
'''

'''
The Mac problems have been significant enough to warrant the addition of a series of settings that allow
users to turn specific patches and features on or off depending on their setup.  There is not enough information
available to make this process more atuomatic.

'''


# Dictionary of Mac Patches.  Used to find the key in the global settings and the default value
MAC_PATCH_DICT = {
    'Enable No Titlebar Patch': ('-mac feature enable no titlebar patch-', False),
    'Disable Modal Windows': ('-mac feature disable modal windows-', True),
    'Disable Grab Anywhere with Titlebar': ('-mac feature disable grab anywhere with titlebar-', True),
    'Set Alpha Channel to 0.99 for MacOS >= 12.3': ('-mac feature disable Alpha 0.99', True),
}


def _read_mac_global_settings():
    """
    Reads the settings from the PySimpleGUI Global Settings and sets variables that
    are used at runtime to control how certain features behave
    """

    global ENABLE_MAC_MODAL_DISABLE_PATCH
    global ENABLE_MAC_NOTITLEBAR_PATCH
    global ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR
    global ENABLE_MAC_ALPHA_99_PATCH

    ENABLE_MAC_MODAL_DISABLE_PATCH = pysimplegui_user_settings.get(MAC_PATCH_DICT['Disable Modal Windows'][0], MAC_PATCH_DICT['Disable Modal Windows'][1])
    ENABLE_MAC_NOTITLEBAR_PATCH = pysimplegui_user_settings.get(MAC_PATCH_DICT['Enable No Titlebar Patch'][0], MAC_PATCH_DICT['Enable No Titlebar Patch'][1])
    ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR = pysimplegui_user_settings.get(
        MAC_PATCH_DICT['Disable Grab Anywhere with Titlebar'][0],
        MAC_PATCH_DICT['Disable Grab Anywhere with Titlebar'][1],
    )
    ENABLE_MAC_ALPHA_99_PATCH = pysimplegui_user_settings.get(
        MAC_PATCH_DICT['Set Alpha Channel to 0.99 for MacOS >= 12.3'][0],
        MAC_PATCH_DICT['Set Alpha Channel to 0.99 for MacOS >= 12.3'][1],
    )


def _mac_should_apply_notitlebar_patch():
    """
    Uses a combination of the tkinter version number and the setting from the global settings
    to determine if the notitlebar patch should be applied

    :return:    True if should apply the no titlebar patch on the Mac
    :rtype:     (bool)
    """

    if not running_mac():
        return False

    try:
        tver = [int(n) for n in framework_version.split('.')]
        if tver[0] == 8 and tver[1] == 6 and tver[2] < 10 and ENABLE_MAC_NOTITLEBAR_PATCH:
            return True
    except Exception as e:
        warnings.warn(f'Exception while trying to parse tkinter version {framework_version} Error = {e}', UserWarning)

    return False


def _mac_should_set_alpha_to_99():

    if not running_mac():
        return False

    if not ENABLE_MAC_ALPHA_99_PATCH:
        return False

    # ONLY enable this patch for tkinter version 8.6.12
    if framework_version != '8.6.12':
        return False

    # At this point, we're running a Mac and the alpha patch is enabled
    # Final check is to see if Mac OS version is 12.3 or later
    try:
        platform_mac_ver = platform.mac_ver()[0]
        mac_ver = platform_mac_ver.split('.') if '.' in platform_mac_ver else (platform_mac_ver, 0)
        if (int(mac_ver[0]) >= 12 and int(mac_ver[1]) >= 3) or int(mac_ver[0]) >= 13:
            # print("Mac OS Version is {} and patch enabled so applying the patch".format(platform_mac_ver))
            return True
    except Exception as e:
        warnings.warn(f'_mac_should_seet_alpha_to_99 Exception while trying check mac_ver. Error = {e}', UserWarning)
        return False

    return False


def main_mac_feature_control():
    """
    Window to set settings that will be used across all PySimpleGUI programs that choose to use them.
    Use set_options to set the path to the folder for all PySimpleGUI settings.

    :return: True if settings were changed
    :rtype:  (bool)
    """

    current_theme = theme()
    theme('dark red')

    layout = [
        [T('Mac PySimpleGUI Feature Control', font='DEFAIULT 18')],
        [T('Use this window to enable / disable features.')],
        [T('Unfortunately, on some releases of tkinter on the Mac, there are problems that')],
        [T('create the need to enable and disable sets of features. This window facilitates the control.')],
        [T('Feature Control / Settings', font='_ 16 bold')],
        [T('You are running tkinter version:', font='_ 12 bold'), T(framework_version, font='_ 12 bold')],
    ]

    for key, value in MAC_PATCH_DICT.items():
        layout += [[Checkbox(key, k=value[0], default=pysimplegui_user_settings.get(value[0], value[1]))]]
    layout += [
        [T('Currently the no titlebar patch ' + ('WILL' if _mac_should_apply_notitlebar_patch() else 'WILL NOT') + ' be applied')],
        [T('The no titlebar patch will ONLY be applied on tkinter versions < 8.6.10')],
    ]
    layout += [[Button('Ok'), Button('Cancel')]]

    window = Window('Mac Feature Control', layout, keep_on_top=True, finalize=True)
    while True:
        event, values = window.read()
        if event in ('Cancel', WIN_CLOSED):
            break
        if event == 'Ok':
            for key, value in values.items():
                print(f'setting {key} to {value}')
                pysimplegui_user_settings.set(key, value)
            break
    window.close()
    theme(current_theme)


'''
'########::'########:'########::'##::::'##::'######::::'######:::'########:'########::
 ##.... ##: ##.....:: ##.... ##: ##:::: ##:'##... ##::'##... ##:: ##.....:: ##.... ##:
 ##:::: ##: ##::::::: ##:::: ##: ##:::: ##: ##:::..::: ##:::..::: ##::::::: ##:::: ##:
 ##:::: ##: ######::: ########:: ##:::: ##: ##::'####: ##::'####: ######::: ########::
 ##:::: ##: ##...:::: ##.... ##: ##:::: ##: ##::: ##:: ##::: ##:: ##...:::: ##.. ##:::
 ##:::: ##: ##::::::: ##:::: ##: ##:::: ##: ##::: ##:: ##::: ##:: ##::::::: ##::. ##::
 ########:: ########: ########::. #######::. ######:::. ######::: ########: ##:::. ##:
........:::........::........::::.......::::......:::::......::::........::..:::::..::
'''

#####################################################################################################
# Debugger
#####################################################################################################


red_x = b'R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=='


class _Debugger:
    debugger = None
    DEBUGGER_MAIN_WINDOW_THEME = 'dark grey 13'
    DEBUGGER_POPOUT_THEME = 'dark grey 13'
    WIDTH_VARIABLES = 23
    WIDTH_RESULTS = 46

    WIDTH_WATCHER_VARIABLES = 20
    WIDTH_WATCHER_RESULTS = 60

    WIDTH_LOCALS = 80
    NUM_AUTO_WATCH = 9

    MAX_LINES_PER_RESULT_FLOATING = 4
    MAX_LINES_PER_RESULT_MAIN = 3

    DEBUGGER_POPOUT_WINDOW_FONT = 'Sans 8'
    DEBUGGER_VARIABLE_DETAILS_FONT = 'Courier 10'

    '''
        #     #                    ######
        ##   ##   ##   # #    #    #     # ###### #####  #    #  ####   ####  ###### #####
        # # # #  #  #  # ##   #    #     # #      #    # #    # #    # #    # #      #    #
        #  #  # #    # # # #  #    #     # #####  #####  #    # #      #      #####  #    #
        #     # ###### # #  # #    #     # #      #    # #    # #  ### #  ### #      #####
        #     # #    # # #   ##    #     # #      #    # #    # #    # #    # #      #   #
        #     # #    # # #    #    ######  ###### #####   ####   ####   ####  ###### #    #
    '''

    def __init__(self):
        self.watcher_window = None  # type: Window
        self.popout_window = None  # type: Window
        self.local_choices = {}
        self.myrc = ''
        self.custom_watch = ''
        self.locals = {}
        self.globals = {}
        self.popout_choices = {}

    # Includes the DUAL PANE (now 2 tabs)!  Don't forget REPL is there too!
    def _build_main_debugger_window(self, location=(None, None)):
        old_theme = theme()
        theme(_Debugger.DEBUGGER_MAIN_WINDOW_THEME)

        def InVar(key1):
            row1 = [
                T('    '),
                I(key=key1, size=(_Debugger.WIDTH_VARIABLES, 1)),
                T('', key=key1 + 'CHANGED_', size=(_Debugger.WIDTH_RESULTS, 1)),
                B('Detail', key=key1 + 'DETAIL_'),
                B('Obj', key=key1 + 'OBJ_'),
            ]
            return row1

        variables_frame = [
            InVar('_VAR0_'),
            InVar('_VAR1_'),
            InVar('_VAR2_'),
        ]

        interactive_frame = [
            [
                T('>>> '),
                In(
                    size=(83, 1),
                    key='-REPL-',
                    tooltip='Type in any "expression" or "statement"\n and it will be disaplayed below.\nPress RETURN KEY instead of "Go"\nbutton for faster use',
                ),
                B('Go', bind_return_key=True, visible=True),
            ],
            [Multiline(size=(93, 26), key='-OUTPUT-', autoscroll=True, do_not_clear=True)],
        ]

        autowatch_frame = [
            [
                Button('Choose Variables To Auto Watch', key='-LOCALS-'),
                Button('Clear All Auto Watches'),
                Button('Show All Variables', key='-SHOW_ALL-'),
                Button('Locals', key='-ALL_LOCALS-'),
                Button('Globals', key='-GLOBALS-'),
                Button('Popout', key='-POPOUT-'),
            ]
        ]

        var_layout = []
        for i in range(_Debugger.NUM_AUTO_WATCH):
            var_layout.append(
                [
                    T('', size=(_Debugger.WIDTH_WATCHER_VARIABLES, 1), key='_WATCH%s_' % i),
                    T(
                        '',
                        size=(_Debugger.WIDTH_WATCHER_RESULTS, _Debugger.MAX_LINES_PER_RESULT_MAIN),
                        key='_WATCH%s_RESULT_' % i,
                    ),
                ]
            )

        col1 = [
            # [Frame('Auto Watches', autowatch_frame+variable_values, title_color='blue')]
            [Frame('Auto Watches', autowatch_frame + var_layout, title_color=theme_button_color()[0])]
        ]

        col2 = [
            [
                Frame('Variables or Expressions to Watch', variables_frame, title_color=theme_button_color()[0]),
            ],
            [
                Frame(
                    'REPL-Light - Press Enter To Execute Commands',
                    interactive_frame,
                    title_color=theme_button_color()[0],
                ),
            ],
        ]

        # Tab based layout
        layout = [
            [Text('Debugging: ' + self._find_users_code())],
            [TabGroup([[Tab('Variables', col1), Tab('REPL & Watches', col2)]])],
        ]

        # ------------------------------- Create main window -------------------------------
        window = Window(
            'PySimpleGUI Debugger',
            layout,
            icon=PSG_DEBUGGER_LOGO,
            margins=(0, 0),
            location=location,
            keep_on_top=True,
            right_click_menu=[
                [''],
                [
                    'Exit',
                ],
            ],
        )

        Window._read_call_from_debugger = True
        window.finalize()
        Window._read_call_from_debugger = False

        window.Element('_VAR1_').SetFocus()
        self.watcher_window = window
        theme(old_theme)
        return window

    '''
        #     #                    #######                               #
        ##   ##   ##   # #    #    #       #    # ###### #    # #####    #        ####   ####  #####
        # # # #  #  #  # ##   #    #       #    # #      ##   #   #      #       #    # #    # #    #
        #  #  # #    # # # #  #    #####   #    # #####  # #  #   #      #       #    # #    # #    #
        #     # ###### # #  # #    #       #    # #      #  # #   #      #       #    # #    # #####
        #     # #    # # #   ##    #        #  #  #      #   ##   #      #       #    # #    # #
        #     # #    # # #    #    #######   ##   ###### #    #   #      #######  ####   ####  #
    '''

    def _refresh_main_debugger_window(self, mylocals, myglobals):
        if not self.watcher_window:  # if there is no window setup, nothing to do
            return False
        event, values = self.watcher_window.read(timeout=1)
        if event in (None, 'Exit', '_EXIT_', '-EXIT-'):  # EXIT BUTTON / X BUTTON
            try:
                self.watcher_window.close()
            except:
                pass
            self.watcher_window = None
            return False
        # ------------------------------- Process events from REPL Tab -------------------------------
        cmd = values['-REPL-']  # get the REPL entered
        # BUTTON - GO (NOTE - This button is invisible!!)
        if event == 'Go':  # GO BUTTON
            self.watcher_window.Element('-REPL-').Update('')
            self.watcher_window.Element('-OUTPUT-').Update(f'>>> {cmd}\n', append=True, autoscroll=True)

            try:
                result = eval(f'{cmd}', myglobals, mylocals)
            except Exception:
                if sys.version_info[0] < 3:
                    result = 'Not available in Python 2'
                else:
                    try:
                        result = exec(f'{cmd}', myglobals, mylocals)
                    except Exception as e:
                        result = f'Exception {e}\n'

            self.watcher_window.Element('-OUTPUT-').Update(f'{result}\n', append=True, autoscroll=True)
        # BUTTON - DETAIL
        elif event.endswith('_DETAIL_'):  # DETAIL BUTTON
            var = values[f'_VAR{event[4]}_']
            try:
                result = str(eval(str(var), myglobals, mylocals))
            except:
                result = ''
            old_theme = theme()
            theme(_Debugger.DEBUGGER_MAIN_WINDOW_THEME)
            popup_scrolled(
                str(values[f'_VAR{event[4]}_']) + '\n' + result,
                title=var,
                non_blocking=True,
                font=_Debugger.DEBUGGER_VARIABLE_DETAILS_FONT,
            )
            theme(old_theme)
        # BUTTON - OBJ
        elif event.endswith('_OBJ_'):  # OBJECT BUTTON
            var = values[f'_VAR{event[4]}_']
            try:
                result = obj_to_string_single_obj(mylocals[var])
            except Exception:
                try:
                    result = eval(f'{var}', myglobals, mylocals)
                    result = obj_to_string_single_obj(result)
                except Exception as e:
                    result = f'{e}\nError showing object {var}'
            old_theme = theme()
            theme(_Debugger.DEBUGGER_MAIN_WINDOW_THEME)
            popup_scrolled(
                str(var) + '\n' + str(result),
                title=var,
                non_blocking=True,
                font=_Debugger.DEBUGGER_VARIABLE_DETAILS_FONT,
            )
            theme(old_theme)
        # ------------------------------- Process Watch Tab -------------------------------
        # BUTTON - Choose Locals to see
        elif event == '-LOCALS-':  # Show all locals BUTTON
            self._choose_auto_watches(mylocals)
        # BUTTON - Locals (quick popup)
        elif event == '-ALL_LOCALS-':
            self._display_all_vars('All Locals', mylocals)
        # BUTTON - Globals (quick popup)
        elif event == '-GLOBALS-':
            self._display_all_vars('All Globals', myglobals)
        # BUTTON - clear all
        elif event == 'Clear All Auto Watches':
            if popup_yes_no('Do you really want to clear all Auto-Watches?', 'Really Clear??') == 'Yes':
                self.local_choices = {}
                self.custom_watch = ''
        # BUTTON - Popout
        elif event == '-POPOUT-':
            if not self.popout_window:
                self._build_floating_window()
        # BUTTON - Show All
        elif event == '-SHOW_ALL-':
            for key in self.locals:
                self.local_choices[key] = not key.startswith('_')

        # -------------------- Process the manual "watch list" ------------------
        for i in range(3):
            key = f'_VAR{i}_'
            out_key = f'_VAR{i}_CHANGED_'
            self.myrc = ''
            if self.watcher_window.Element(key):
                var = values[key]
                try:
                    result = eval(str(var), myglobals, mylocals)
                except:
                    result = ''
                self.watcher_window.Element(out_key).Update(str(result))
            else:
                self.watcher_window.Element(out_key).Update('')

        # -------------------- Process the automatic "watch list" ------------------
        slot = 0
        for key in self.local_choices:
            if key == '-CUSTOM_WATCH-':
                continue
            if self.local_choices[key]:
                self.watcher_window.Element(f'_WATCH{slot}_').Update(key)
                try:
                    self.watcher_window.Element(f'_WATCH{slot}_RESULT_', silent_on_error=True).Update(mylocals[key])
                except:
                    self.watcher_window.Element(f'_WATCH{slot}_RESULT_').Update('')
                slot += 1

            if slot + int(self.custom_watch not in (None, '')) >= _Debugger.NUM_AUTO_WATCH:
                break
        # If a custom watch was set, display that value in the window
        if self.custom_watch:
            self.watcher_window.Element(f'_WATCH{slot}_').Update(self.custom_watch)
            try:
                self.myrc = eval(self.custom_watch, myglobals, mylocals)
            except:
                self.myrc = ''
            self.watcher_window.Element(f'_WATCH{slot}_RESULT_').Update(self.myrc)
            slot += 1
        # blank out all of the slots not used (blank)
        for i in range(slot, _Debugger.NUM_AUTO_WATCH):
            self.watcher_window.Element(f'_WATCH{i}_').Update('')
            self.watcher_window.Element(f'_WATCH{i}_RESULT_').Update('')

        return True  # return indicating the window stayed open

    def _find_users_code(self):
        try:  # lots can go wrong so wrapping the entire thing
            trace_details = traceback.format_stack()
            file_info_pysimplegui, error_message = None, ''
            for line in reversed(trace_details):
                if __file__ not in line:
                    file_info_pysimplegui = line.split(',')[0]
                    error_message = line
                    break
            if file_info_pysimplegui is None:
                return ''
            error_parts = None
            if error_message != '':
                error_parts = error_message.split(', ')
                if len(error_parts) < 4:
                    error_message = error_parts[0] + '\n' + error_parts[1] + '\n' + ''.join(error_parts[2:])
            if error_parts is None:
                print('*** Error popup attempted but unable to parse error details ***')
                print(trace_details)
                return ''
            filename = error_parts[0][error_parts[0].index('File ') + 5 :]
            return filename
        except:
            return

    '''
        ######                                 #     #
        #     #  ####  #####  #    # #####     #  #  # # #    # #####   ####  #    #
        #     # #    # #    # #    # #    #    #  #  # # ##   # #    # #    # #    #
        ######  #    # #    # #    # #    #    #  #  # # # #  # #    # #    # #    #
        #       #    # #####  #    # #####     #  #  # # #  # # #    # #    # # ## #
        #       #    # #      #    # #         #  #  # # #   ## #    # #    # ##  ##
        #        ####  #       ####  #          ## ##  # #    # #####   ####  #    #

        ######                                    #                     #     #
        #     # #    # #    # #####   ####       # #   #      #         #     #   ##   #####   ####
        #     # #    # ##  ## #    # #          #   #  #      #         #     #  #  #  #    # #
        #     # #    # # ## # #    #  ####     #     # #      #         #     # #    # #    #  ####
        #     # #    # #    # #####       #    ####### #      #          #   #  ###### #####       #
        #     # #    # #    # #      #    #    #     # #      #           # #   #    # #   #  #    #
        ######   ####  #    # #       ####     #     # ###### ######       #    #    # #    #  ####
    '''
    # displays them into a single text box

    def _display_all_vars(self, title, dict):
        out_text = title + '\n'
        sorted_dict = {}
        for key in sorted(dict.keys()):
            sorted_dict[key] = dict[key]
        for key in sorted_dict:
            value = dict[key]
            wrapped_text = str(value)
            out_text += f'{key} - {wrapped_text}\n'
        old_theme = theme()
        theme(_Debugger.DEBUGGER_MAIN_WINDOW_THEME)
        popup_scrolled(
            out_text,
            title=title,
            non_blocking=True,
            font=_Debugger.DEBUGGER_VARIABLE_DETAILS_FONT,
            keep_on_top=True,
            icon=PSG_DEBUGGER_LOGO,
        )
        theme(old_theme)

    '''
        #####                                        #     #
       #     # #    #  ####   ####   ####  ######    #  #  #   ##   #####  ####  #    #
       #       #    # #    # #    # #      #         #  #  #  #  #    #   #    # #    #
       #       ###### #    # #    #  ####  #####     #  #  # #    #   #   #      ######
       #       #    # #    # #    #      # #         #  #  # ######   #   #      #    #
       #     # #    # #    # #    # #    # #         #  #  # #    #   #   #    # #    #
        #####  #    #  ####   ####   ####  ######     ## ##  #    #   #    ####  #    #

        #     #                                                       #     #
        #     #   ##   #####  #   ##   #####  #      ######  ####     #  #  # # #    #
        #     #  #  #  #    # #  #  #  #    # #      #      #         #  #  # # ##   #
        #     # #    # #    # # #    # #####  #      #####   ####     #  #  # # # #  #
         #   #  ###### #####  # ###### #    # #      #           #    #  #  # # #  # #
          # #   #    # #   #  # #    # #    # #      #      #    #    #  #  # # #   ##
           #    #    # #    # # #    # #####  ###### ######  ####      ## ##  # #    #
    '''

    def _choose_auto_watches(self, my_locals):
        old_theme = theme()
        theme(_Debugger.DEBUGGER_MAIN_WINDOW_THEME)
        num_cols = 3
        cur_col = 0
        layout = [[Text('Choose your "Auto Watch" variables', font='ANY 14', text_color='red')]]
        longest_line = max([len(key) for key in my_locals])
        line = []
        sorted_dict = {}
        for key in sorted(my_locals.keys()):
            sorted_dict[key] = my_locals[key]
        for key in sorted_dict:
            line.append(
                CB(
                    key,
                    key=key,
                    size=(longest_line, 1),
                    default=self.local_choices[key] if key in self.local_choices else False,
                )
            )
            if cur_col + 1 == num_cols:
                cur_col = 0
                layout.append(line)
                line = []
            else:
                cur_col += 1
        if cur_col:
            layout.append(line)

        layout += [
            [
                Text('Custom Watch (any expression)'),
                Input(default_text=self.custom_watch, size=(40, 1), key='-CUSTOM_WATCH-'),
            ]
        ]
        layout += [[Ok(), Cancel(), Button('Clear All'), Button('Select [almost] All', key='-AUTO_SELECT-')]]

        window = Window('Choose Watches', layout, icon=PSG_DEBUGGER_LOGO, finalize=True, keep_on_top=True)

        while True:  # event loop
            event, values = window.read()
            if event in (None, 'Cancel', '-EXIT-'):
                break
            elif event == 'Ok':
                self.local_choices = values
                self.custom_watch = values['-CUSTOM_WATCH-']
                break
            elif event == 'Clear All':
                popup_quick_message(
                    'Cleared Auto Watches',
                    auto_close=True,
                    auto_close_duration=3,
                    non_blocking=True,
                    text_color='red',
                    font='ANY 18',
                )
                for key in sorted_dict:
                    window.Element(key).Update(False)
                window.Element('-CUSTOM_WATCH-').Update('')
            elif event == 'Select All':
                for key in sorted_dict:
                    window.Element(key).Update(False)
            elif event == '-AUTO_SELECT-':
                for key in sorted_dict:
                    window.Element(key).Update(not key.startswith('_'))

        # exited event loop
        window.Close()
        theme(old_theme)

    '''
        ######                            #######
        #     # #    # # #      #####     #       #       ####    ##   ##### # #    #  ####
        #     # #    # # #      #    #    #       #      #    #  #  #    #   # ##   # #    #
        ######  #    # # #      #    #    #####   #      #    # #    #   #   # # #  # #
        #     # #    # # #      #    #    #       #      #    # ######   #   # #  # # #  ###
        #     # #    # # #      #    #    #       #      #    # #    #   #   # #   ## #    #
        ######   ####  # ###### #####     #       ######  ####  #    #   #   # #    #  ####

        #     #
        #  #  # # #    # #####   ####  #    #
        #  #  # # ##   # #    # #    # #    #
        #  #  # # # #  # #    # #    # #    #
        #  #  # # #  # # #    # #    # # ## #
        #  #  # # #   ## #    # #    # ##  ##
         ## ##  # #    # #####   ####  #    #
    '''

    def _build_floating_window(self, location=(None, None)):
        """

        :param location:
        :type location:

        """
        if self.popout_window:  # if floating window already exists, close it first
            self.popout_window.Close()
        old_theme = theme()
        theme(_Debugger.DEBUGGER_POPOUT_THEME)
        num_cols = 2
        width_var = 15
        width_value = 30
        layout = []
        line = []
        col = 0
        # self.popout_choices = self.local_choices
        self.popout_choices = {}
        if self.popout_choices == {}:  # if nothing chosen, then choose all non-_ variables
            for key in sorted(self.locals.keys()):
                self.popout_choices[key] = not key.startswith('_')

        width_var = max([len(key) for key in self.popout_choices])
        for key in self.popout_choices:
            if self.popout_choices[key] is True:
                value = str(self.locals.get(key))
                h = min(len(value) // width_value + 1, _Debugger.MAX_LINES_PER_RESULT_FLOATING)
                line += [
                    Text(f'{key}', size=(width_var, 1), font=_Debugger.DEBUGGER_POPOUT_WINDOW_FONT),
                    Text(' = ', font=_Debugger.DEBUGGER_POPOUT_WINDOW_FONT),
                    Text(value, key=key, size=(width_value, h), font=_Debugger.DEBUGGER_POPOUT_WINDOW_FONT),
                ]
                if col + 1 < num_cols:
                    line += [VerticalSeparator(), T(' ')]
                col += 1
            if col >= num_cols:
                layout.append(line)
                line = []
                col = 0
        if col != 0:
            layout.append(line)
        layout = [[T(SYMBOL_X, enable_events=True, key='-EXIT-', font='_ 7')], [Column(layout)]]

        Window._read_call_from_debugger = True
        self.popout_window = Window(
            'Floating',
            layout,
            alpha_channel=0,
            no_titlebar=True,
            grab_anywhere=True,
            element_padding=(0, 0),
            margins=(0, 0),
            keep_on_top=True,
            right_click_menu=['&Right', ['Debugger::RightClick', 'Exit::RightClick']],
            location=location,
            finalize=True,
        )
        Window._read_call_from_debugger = False

        if location == (None, None):
            screen_size = self.popout_window.GetScreenDimensions()
            self.popout_window.Move(screen_size[0] - self.popout_window.Size[0], 0)
        self.popout_window.SetAlpha(1)
        theme(old_theme)
        return True

    '''
        ######
        #     # ###### ###### #####  ######  ####  #    #
        #     # #      #      #    # #      #      #    #
        ######  #####  #####  #    # #####   ####  ######
        #   #   #      #      #####  #           # #    #
        #    #  #      #      #   #  #      #    # #    #
        #     # ###### #      #    # ######  ####  #    #

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

        #     #
        #  #  # # #    # #####   ####  #    #
        #  #  # # ##   # #    # #    # #    #
        #  #  # # # #  # #    # #    # #    #
        #  #  # # #  # # #    # #    # # ## #
        #  #  # # #   ## #    # #    # ##  ##
         ## ##  # #    # #####   ####  #    #
    '''

    def _refresh_floating_window(self):
        if not self.popout_window:
            return
        for key in self.popout_choices:
            if self.popout_choices[key] is True and key in self.locals:
                if key is not None and self.popout_window is not None:
                    self.popout_window.Element(key, silent_on_error=True).Update(self.locals.get(key))
        event, values = self.popout_window.read(timeout=5)
        if event in (None, '_EXIT_', 'Exit::RightClick', '-EXIT-'):
            self.popout_window.Close()
            self.popout_window = None
        elif event == 'Debugger::RightClick':
            show_debugger_window()


# 888     888                                .d8888b.         d8888 888 888          888      888
# 888     888                               d88P  Y88b       d88888 888 888          888      888
# 888     888                               888    888      d88P888 888 888          888      888
# 888     888 .d8888b   .d88b.  888d888     888            d88P 888 888 888  8888b.  88888b.  888  .d88b.
# 888     888 88K      d8P  Y8b 888P"       888           d88P  888 888 888     "88b 888 "88b 888 d8P  Y8b
# 888     888 "Y8888b. 88888888 888         888    888   d88P   888 888 888 .d888888 888  888 888 88888888
# Y88b. .d88P      X88 Y8b.     888         Y88b  d88P  d8888888888 888 888 888  888 888 d88P 888 Y8b.
#  "Y88888P"   88888P'  "Y8888  888          "Y8888P"  d88P     888 888 888 "Y888888 88888P"  888  "Y8888

# 8888888888                            888    d8b
# 888                                   888    Y8P
# 888                                   888
# 8888888    888  888 88888b.   .d8888b 888888 888  .d88b.  88888b.  .d8888b
# 888        888  888 888 "88b d88P"    888    888 d88""88b 888 "88b 88K
# 888        888  888 888  888 888      888    888 888  888 888  888 "Y8888b.
# 888        Y88b 888 888  888 Y88b.    Y88b.  888 Y88..88P 888  888      X88
# 888         "Y88888 888  888  "Y8888P  "Y888 888  "Y88P"  888  888  88888P'


def show_debugger_window(location=(None, None), *args):
    """
    Shows the large main debugger window
    :param location: Locations (x,y) on the screen to place upper left corner of the window
    :type location:  (int, int)
    :return:         None
    :rtype:          None
    """
    if _Debugger.debugger is None:
        _Debugger.debugger = _Debugger()
    debugger = _Debugger.debugger
    frame = inspect.currentframe()
    try:
        debugger.locals = frame.f_back.f_locals
        debugger.globals = frame.f_back.f_globals
    finally:
        del frame

    if not debugger.watcher_window:
        debugger.watcher_window = debugger._build_main_debugger_window(location=location)
    return True


def show_debugger_popout_window(location=(None, None), *args):
    """
    Shows the smaller "popout" window.  Default location is the upper right corner of your screen

    :param location: Locations (x,y) on the screen to place upper left corner of the window
    :type location:  (int, int)
    :return:         None
    :rtype:          None
    """
    if _Debugger.debugger is None:
        _Debugger.debugger = _Debugger()
    debugger = _Debugger.debugger
    frame = inspect.currentframe()
    try:
        debugger.locals = frame.f_back.f_locals
        debugger.globals = frame.f_back.f_globals
    finally:
        del frame
    if debugger.popout_window:
        debugger.popout_window.Close()
        debugger.popout_window = None
    debugger._build_floating_window(location=location)


def _refresh_debugger():
    """
    Refreshes the debugger windows. USERS should NOT be calling this function. Within PySimpleGUI it is called for the USER every time the Window.Read function is called.

    :return: return code False if user closed the main debugger window.
    :rtype:  (bool)
    """
    if _Debugger.debugger is None:
        _Debugger.debugger = _Debugger()
    debugger = _Debugger.debugger
    Window._read_call_from_debugger = True
    rc = None
    # frame = inspect.currentframe()
    # frame = inspect.currentframe().f_back

    try:
        frame, *others = inspect.stack()[1]
        try:
            debugger.locals = frame.f_back.f_locals
            debugger.globals = frame.f_back.f_globals
        finally:
            del frame
        if debugger.popout_window:
            rc = debugger._refresh_floating_window()
        if debugger.watcher_window:
            rc = debugger._refresh_main_debugger_window(debugger.locals, debugger.globals)
        Window._read_call_from_debugger = False
        return rc
    except:
        return None


def _debugger_window_is_open():
    """
    Determines if one of the debugger window is currently open
    :return: returns True if the popout window or the main debug window is open
    :rtype: (bool)
    """

    if _Debugger.debugger is None:
        return False
    debugger = _Debugger.debugger
    if debugger.popout_window or debugger.watcher_window:
        return True
    return False


def get_versions():
    """
    Returns a human-readable string of version numbers for:

    Python version
    Platform (Win, Mac, Linux)
    Platform version (tuple with information from the platform module)
    PySimpleGUI Port (PySimpleGUI in this case)
    tkinter version
    PySimpleGUI version
    The location of the PySimpleGUI.py file

    The format is a newline between each value and descriptive text for each line

    :return:
    :rtype:  str
    """
    if running_mac():
        platform_name, platform_ver = 'Mac', platform.mac_ver()
    elif running_windows():
        platform_name, platform_ver = 'Windows', platform.win32_ver()
    elif running_linux():
        platform_name, platform_ver = 'Linux', platform.libc_ver()
    else:
        platform_name, platform_ver = 'Unknown platorm', 'Unknown platform version'

    versions = 'Python Interpeter: {}\nPython version: {}.{}.{}\nPlatform: {}\nPlatform version: {}\nPort: {}\ntkinter version: {}\nPySimpleGUI version: {}\nPySimpleGUI filename: {}'.format(
        sys.executable,
        sys.version_info.major,
        sys.version_info.minor,
        sys.version_info.micro,
        platform_name,
        platform_ver,
        port,
        tclversion_detailed,
        ver,
        __file__,
    )
    return versions


'''
M"""""`'"""`YM
M  mm.  mm.  M
M  MMM  MMM  M .d8888b. 88d888b. .d8888b.
M  MMM  MMM  M 88'  `88 88'  `88 88ooood8
M  MMM  MMM  M 88.  .88 88       88.  ...
M  MMM  MMM  M `88888P' dP       `88888P'
MMMMMMMMMMMMMM

M#"""""""'M                             .d8888P dP   dP
##  mmmm. `M                            88'     88   88
#'        .M .d8888b. .d8888b. .d8888b. 88baaa. 88aaa88
M#  MMMb.'YM 88'  `88 Y8ooooo. 88ooood8 88` `88      88
M#  MMMM'  M 88.  .88       88 88.  ... 8b. .d8      88
M#       .;M `88888P8 `88888P' `88888P' `Y888P'      dP
M#########M

M""M
M  M
M  M 88d8b.d8b. .d8888b. .d8888b. .d8888b. .d8888b.
M  M 88'`88'`88 88'  `88 88'  `88 88ooood8 Y8ooooo.
M  M 88  88  88 88.  .88 88.  .88 88.  ...       88
M  M dP  dP  dP `88888P8 `8888P88 `88888P' `88888P'
MMMM                          .88
                          d8888P
'''


'''

90 x 90 pixel images

These images are intentionally a little large so that you can use the image_subsample to reduce their size.

This offers more flexibility for use in a main window (larger) or perhaps a titlebar (smaller)

'''

HEART_FLAT_BASE64 = b' '


HEART_3D_BASE64 = b' '

PYTHON_COLORED_HEARTS_BASE64 = b' '

RED_X_BASE64 = b' '

GREEN_CHECK_BASE64 = b'iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAAA4qEECAAAJV0lEQVR4nO2cTWwc5RnHf8/M7Dq7ttdxIIIUcqGA1BQU6Ac9VSkp0NwoJE5PJJygKki9tIIEO7ND3ICEeqJUJYcqCYdKDoS0lWgpH21KuVShH/TjUolLkIpKguO1vWvvfDw9zOxH1l8zjnc3Xs/vFEXy7uzPz/7f93nnGUNKSkpKSkpKSkpKSkpKzyFMYDKC2e0L2TjYGN2+hN5DkXoVP1s4wdjgDwB4jEw3L6u30CguAJzCCV4YUp4bUuzC94BlZaclHx9hPwb78bELp8jJQaa1yrx65OQljhSe4DguLy8uOxUdhzAuDE5HkvvlEWbVRcgSYDKnHnn5CXbhSR5fXHYqemXCSj6Nj1M4Qb88wrR6EMkUpC47Jy8yFsm2sa58kZSlUYTTUVw4hRPkjIPMBC6ySDwoioHPJrEo65M8W3qJx8hwHBdS0UujTZVcLJwkLweY0cUlN35GEQJyYlLRJ3BKP2UEk9P4qejFWTyTibGFq1V2ViwqPMXRqRcYwUgzupXmha9YOJlIMoSZ7ROQEZBgJ6DsQNKKbmZBJsvBFeOilQCPQbGo6Ens0qNRdARpRddollwsnAwXPq0mkgwug2Ixq69glx7Fjr4ZoGlFhyzM5KSVrLgMSIZZfQWndKBWyYBCuo9erhlJIrnKgJGhrKdwSgeYwGSiIRnS7V1Dci2Tp9XDuLLZWJZaJdcyOTw6DZCGZNjIFR0eEDVJNsKFL4lkIsllPVVf+BaRDBu1olfTjCzEpX/pTG5lI1Z0Q7JdOEVeDqwik0PJtUweWZjJrWws0VfbjISv4TJghJlcLB2sL3yLxEUzGyc62tiMsEwl19gYFd2OZiRGXDSzESq67c1IHHq7ojvUjMShlyu6Y81IHHqzojvcjMSh9yq6C81IHHqtorvSjMShd0R3sRmJQ29ER5ebkTjEE21j8EWE/fhr8aZrTFhvgoaZbBxgJqgiZBO8xsJMXqNKblzkStgYOAQL/n2tUB9UKfy8W81IHJbPaBsLh4DRgS8wVvgWDkHrBE5Xscni4Bk69H2GjEeY1fluNCNxWLqid2FxDo9nCp8ny/v0yQ1U/L04M2d4mQyPhxM4XSOaAio4N391Wqbf0ECHUQzixuEaNiNxWLyi7Ujy6OBtZHkPU25gTj2yxgSjAw8vNlvWUWwsjuMOjt30tWlj5k019HoChPiL+5o2I3FYeGFhXHg8PXg7A/I2yHaq6gMGJoopwpz/MOMzZ5tnyzpGdH2FwzffM52f+Y1qsAUXH4n9iMOaNyNxuFJ0TfIPB29jSN5BZDvz6iFR9SoayTZw/YdwZs52NEai68uPfu7uSt/sO4oOJ5KsTZVcLB1sx+5iKRqiJzDZj8/TQ7eQ1z9iyk3M68IP0ZAtzLGP8akz0aJUbeuVRpKH7G1fKlmz7yoMJZdsZKgEHcnkVsKMtuuT7LeS1/eXlAy12TLBVyXHBIcH9uJQbeszHJHk3OEbvzJllkPJVYLYkgO8cOELGs3I/s5JBpDGE0XDOzD9NzBl+5KSm1ECTMACZoN9HJt5vS2ZXYuLseu/XO5z30T1uqvO5A7FRTMG1JoQ/2fkje1UtIoR40MIBj7gAXnjDKMD3+Y47ppWdiQ5Yw/dVelzf5tYsi6x8HVYMoSig7Cqze9SDi6QkyxBzFY7lB2OqW4yXmds6KHlHphJxGNkcPAyo1t3ehbvqOr1CSV3rBmJQ6Oldib/ic9ufP2EPjHR2LKlIZtXGRvYy+O49cfEVkO0T87bW+9ys/PnFN0SO5MVRZlnQLJUgsYpXAcXvsVIvutYilpmmyjzwXc4OnOmfmyZhFpcjA7d7fbxFnAdbszrCKfthYJAqfNbuOVodIb78bGxeH7qI6b1XlQvRJXtxXolwcADAkyxjBMjE3YmPIBPcObdLHkTb5JMsk8WEZVJqyRPUiwdBOhWJrdypQQHDxuLF6b/w4zeh+oFsmLFjhEDAx9fTcm99u8Xz47YI1mKaCzZtWZpdPhOt4+3UN2aSHIGUzAuDTK4xytefimKLqFLmdzK4mcD9Q89eBsZOYcl2xLFSEDAgBjGvPHruz++Ze8H2z4If1FLHbHWK3n4TjfrncOQYaoxF76G5MlBb2BPyfn4zx1poBKy8uldmNl/wkwoO9paSdX45b4P79t7esfpsLJaZdclb97pZv3fIxK/rQ4IyGJIwPRgMLS75Fw435Xzlxgs/ZU+F8XI81MfUeLrBPoxfSTZjWSYVVezwYOv3vm718SRULA2/XJr3xw7f5e7Sd9GjPiSw0w2BJnMycCuknPhfG23Euv6OkycOyxXnuaJbGdO/VhNTUhY2WX9lRZLD9ZFFzFx8Hgqv5NB6y2QrVQTZrLIpZybeaDsXPxL/TqvUeLeM2zIzsu7GHJTbCnQfGp2ln+V9rEDwcHjUP8d5M0/APE7vkgyyKWcl9tTcT45f61LhiR3weuyC7eS5z1MuXE1mY2rZxgt7cUevgPLfw9hc+yFL8pk4HK+2n9f+eh/P1gPkiHpuMHVNzUeebGoBOdAbiebYIGtVzKXM17fva7z6d/Wi2RYzVzHSjcHViIgICcGnoIbdXIr0ZTJltu323X+9+F6kgyrHaBZ7HbXfIJJzXDnIkiMRkbxyYiJcDE/n9lTPnpx3cRFM6ufVGptavpkG+UEMRKHmmT4LFPJ3O8eu/Z3F0txdSNhTU2N5PmFCvfgaxDd9r86wn2yic9UxjV2ueOX/75eJcNazN5F00uCYBS3OH7OO0I54XBhK7WFT+Qz5oxvMD75j/UsGdZqyDE8NDLEEc90ho94m3yHirooVuL3UHyyYgKfUuYBjk2tq93FUqztNKmNJQ6e6WwZ9Tb5R6moF8mOR9PCl5njAXd86q+9IBnaMbYbyRZ782iQ11B2gLXiO9UkazBJ1byXdZ7JrbRjPlqww3MMoyF7+RipLXyBTlK1dvVCJrfSvkH0aILJKBaeCXIyHi2QC2XXFz4uMufvZny25yRDOx+tiP6iYVAs/YiKHiYvGcLhhMYdj3omy6e43v29Khk68WhF7SD+SOEQ/XIsWiBNlCBqRi4xL9/stUxupf0PCx2PRnyfLT3HrH+YnFgoLhlMVC9T9nb3uuTOUptgOlI4xI+HlKOFixzqvwNoejwiZW2oCS0WnuBw4Z4r/i9ljWkePUj/ZHubsbFSySkpKSkpKSkpKSkpKSkpKW3g/3+PYisYNf7zAAAAAElFTkSuQmCC'


'''
M""MMMMM""M                                           dP
M  MMMMM  M                                           88
M  MMMMM  M 88d888b. .d8888b. 88d888b. .d8888b. .d888b88 .d8888b.
M  MMMMM  M 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88ooood8
M  `MMM'  M 88.  .88 88.  .88 88       88.  .88 88.  .88 88.  ...
Mb       dM 88Y888P' `8888P88 dP       `88888P8 `88888P8 `88888P'
MMMMMMMMMMM 88            .88
            dP        d8888P
MP""""""`MM                            oo
M  mmmmm..M
M.      `YM .d8888b. 88d888b. dP   .dP dP .d8888b. .d8888b.
MMMMMMM.  M 88ooood8 88'  `88 88   d8' 88 88'  `"" 88ooood8
M. .MMM'  M 88.  ... 88       88 .88'  88 88.  ... 88.  ...
Mb.     .dM `88888P' dP       8888P'   dP `88888P' `88888P'
MMMMMMMMMMM
'''


def __get_linux_distribution():
    line_tuple = ('Linux Distro', 'Unknown', 'No lines Found in //etc//os-release')
    try:
        with open('/etc/os-release') as f:
            data = f.read()
        lines = data.split('\n')
        for line in lines:
            if line.startswith('PRETTY_NAME'):
                line_split = line.split('=')[1].strip('"')
                line_tuple = tuple(line_split.split(' '))
                return line_tuple
    except:
        line_tuple = ('Linux Distro', 'Exception', 'Error reading//processing //etc//os-release')

    return line_tuple


# =========================================================================#
# MP""""""`MM                                                                dP                  dP
# M  mmmmm..M                                                                88                  88
# M.      `YM .d8888b. 88d888b. .d8888b. .d8888b. .d8888b. 88d888b. .d8888b. 88d888b. .d8888b. d8888P
# MMMMMMM.  M 88'  `"" 88'  `88 88ooood8 88ooood8 88ooood8 88'  `88 Y8ooooo. 88'  `88 88'  `88   88
# M. .MMM'  M 88.  ... 88       88.  ... 88.  ... 88.  ... 88    88       88 88    88 88.  .88   88
# Mb.     .dM `88888P' dP       `88888P' `88888P' `88888P' dP    dP `88888P' dP    dP `88888P'   dP
# MMMMMMMMMMM
#
# M"""""`'"""`YM                   oo
# M  mm.  mm.  M
# M  MMM  MMM  M .d8888b. .d8888b. dP .d8888b.
# M  MMM  MMM  M 88'  `88 88'  `88 88 88'  `""
# M  MMM  MMM  M 88.  .88 88.  .88 88 88.  ...
# M  MMM  MMM  M `88888P8 `8888P88 dP `88888P'
# MMMMMMMMMMMMMM               .88
#                          d8888P
# M#"""""""'M                    oo                      M""MMMMM""MM
# ##  mmmm. `M                                           M  MMMMM  MM
# #'        .M .d8888b. .d8888b. dP 88d888b. .d8888b.    M         `M .d8888b. 88d888b. .d8888b.
# M#  MMMb.'YM 88ooood8 88'  `88 88 88'  `88 Y8ooooo.    M  MMMMM  MM 88ooood8 88'  `88 88ooood8
# M#  MMMM'  M 88.  ... 88.  .88 88 88    88       88    M  MMMMM  MM 88.  ... 88       88.  ...
# M#       .;M `88888P' `8888P88 dP dP    dP `88888P'    M  MMMMM  MM `88888P' dP       `88888P'
# M#########M                .88                         MMMMMMMMMMMM
#                        d8888P
# =========================================================================#


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

# MP""""""`MM   dP                       dP               .8888b
# M  mmmmm..M   88                       88               88   "
# M.      `YM d8888P .d8888b. 88d888b. d8888P    .d8888b. 88aaa
# MMMMMMM.  M   88   88'  `88 88'  `88   88      88'  `88 88
# M. .MMM'  M   88   88.  .88 88         88      88.  .88 88
# Mb.     .dM   dP   `88888P8 dP         dP      `88888P' dP
# MMMMMMMMMMM
#
# dP dP                     oo          dP dP
# dP dP                                 dP dP
#       88d8b.d8b. .d8888b. dP 88d888b.
#       88'`88'`88 88'  `88 88 88'  `88
#       88  88  88 88.  .88 88 88    88
#       dP  dP  dP `88888P8 dP dP    dP
#
#
# MM""""""""`M            dP
# MM  mmmmmmmM            88
# M`      MMMM 88d888b. d8888P 88d888b. dP    dP
# MM  MMMMMMMM 88'  `88   88   88'  `88 88    88
# MM  MMMMMMMM 88    88   88   88       88.  .88
# MM        .M dP    dP   dP   dP       `8888P88
# MMMMMMMMMMMM                               .88
#                                        d8888P
# MM"""""""`YM          oo            dP
# MM  mmmmm  M                        88
# M'        .M .d8888b. dP 88d888b. d8888P .d8888b.
# MM  MMMMMMMM 88'  `88 88 88'  `88   88   Y8ooooo.
# MM  MMMMMMMM 88.  .88 88 88    88   88         88
# MM  MMMMMMMM `88888P' dP dP    dP   dP   `88888P'
# MMMMMMMMMMMM

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


# M"""""`'"""`YM          oo
# M  mm.  mm.  M
# M  MMM  MMM  M .d8888b. dP 88d888b.
# M  MMM  MMM  M 88'  `88 88 88'  `88
# M  MMM  MMM  M 88.  .88 88 88    88
# M  MMM  MMM  M `88888P8 dP dP    dP
# MMMMMMMMMMMMMM
#
# MM"""""""`YM                     dP      MM'"""""`MM oo   dP   M""MMMMM""MM          dP
# MM  mmmmm  M                     88      M' .mmm. `M      88   M  MMMMM  MM          88
# M'        .M .d8888b. .d8888b. d8888P    M  MMMMMMMM dP d8888P M         `M dP    dP 88d888b.
# MM  MMMMMMMM 88'  `88 Y8ooooo.   88      M  MMM   `M 88   88   M  MMMMM  MM 88    88 88'  `88
# MM  MMMMMMMM 88.  .88       88   88      M. `MMM' .M 88   88   M  MMMMM  MM 88.  .88 88.  .88
# MM  MMMMMMMM `88888P' `88888P'   dP      MM.     .MM dP   dP   M  MMMMM  MM `88888P' 88Y8888'
# MMMMMMMMMMMM                             MMMMMMMMMMM           MMMMMMMMMMMM
#
# M""M
# M  M
# M  M .d8888b. .d8888b. dP    dP .d8888b.
# M  M Y8ooooo. Y8ooooo. 88    88 88ooood8
# M  M       88       88 88.  .88 88.  ...
# M  M `88888P' `88888P' `88888P' `88888P'
# MMMM


def _github_issue_post_make_markdown(
    issue_type,
    operating_system,
    os_ver,
    psg_port,
    psg_ver,
    gui_ver,
    python_ver,
    python_exp,
    prog_exp,
    used_gui,
    gui_notes,
    cb_docs,
    cb_demos,
    cb_demo_port,
    cb_readme_other,
    cb_command_line,
    cb_issues,
    cb_latest_pypi,
    cb_github,
    detailed_desc,
    code,
    project_details,
    where_found,
):
    body = """
## Type of Issue (Enhancement, Error, Bug, Question)

{}

----------------------------------------

## Environment

#### Operating System

{}  version {}

#### PySimpleGUI Port (tkinter, Qt, Wx, Web)

{}

----------------------------------------

## Versions


#### Python version (`sg.sys.version`)

{}

#### PySimpleGUI Version (`sg.__version__`)

{}

#### GUI Version  (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi)

{}

#### Project details

{}
""".format(
        issue_type, operating_system, os_ver, psg_port, python_ver, psg_ver, gui_ver, project_details
    )

    body2 = """


---------------------

## Your Experience In Months or Years (optional)

{} Years Python programming experience
{} Years Programming experience overall
{} Have used another Python GUI Framework? (tkinter, Qt, etc) (yes/no is fine)
{}

---------------------

## Troubleshooting

These items may solve your problem. Please check those you've done by changing - [ ] to - [X]

- [{}] Searched main docs for your problem  www.PySimpleGUI.org
- [{}] Looked for Demo Programs that are similar to your goal. It is recommend you use the Demo Browser! Demos.PySimpleGUI.org
- [{}] If not tkinter - looked for Demo Programs for specific port
- [{}] For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi)
- [{}] Run your program outside of your debugger (from a command line)
- [{}] Searched through Issues (open and closed) to see if already reported Issues.PySimpleGUI.org
- [{}] Upgraded to the latest official release of PySimpleGUI on PyPI
- [{}] Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released

## Detailed Description

{}

#### Code To Duplicate


```python
{}


```

#### Screenshot, Sketch, or Drawing



""".format(
        python_exp,
        prog_exp,
        used_gui,
        gui_notes,
        cb_docs,
        cb_demos,
        cb_demo_port,
        cb_readme_other,
        cb_command_line,
        cb_issues,
        cb_latest_pypi,
        cb_github,
        detailed_desc,
        code if len(code) > 10 else '# Paste your code here',
    )

    if project_details or where_found:
        body2 += '------------------------'

    if project_details:
        body2 += """
## Watcha Makin?
{}
""".format(
            str(project_details)
        )

    if where_found:
        body2 += """
## How did you find PySimpleGUI?
{}
""".format(
            str(where_found)
        )
    return body + body2


def _github_issue_post_make_github_link(title, body):
    pysimplegui_url = 'https://github.com/spyoungtech/FreeSimpleGui'
    pysimplegui_issues = f'{pysimplegui_url}/issues/new?'

    # Fix body cuz urllib can't do it smfh
    getVars = {'title': str(title), 'body': str(body)}
    return pysimplegui_issues + urllib.parse.urlencode(getVars).replace('%5Cn', '%0D')


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


def _github_issue_post_validate(values, checklist, issue_types):
    issue_type = None
    for itype in issue_types:
        if values[itype]:
            issue_type = itype
            break
    if issue_type is None:
        popup_error('Must choose issue type', keep_on_top=True)
        return False
    if values['-OS WIN-']:
        os_ver = values['-OS WIN VER-']
    elif values['-OS LINUX-']:
        os_ver = values['-OS LINUX VER-']
    elif values['-OS MAC-']:
        os_ver = values['-OS MAC VER-']
    elif values['-OS OTHER-']:
        os_ver = values['-OS OTHER VER-']
    else:
        popup_error('Must choose Operating System', keep_on_top=True)
        return False

    if os_ver == '':
        popup_error('Must fill in an OS Version', keep_on_top=True)
        return False

    checkboxes = any([values[('-CB-', i)] for i in range(len(checklist))])
    if not checkboxes:
        popup_error('None of the checkboxes were checked.... you need to have tried something...anything...', keep_on_top=True)
        return False

    title = values['-TITLE-'].strip()
    if len(title) == 0:
        popup_error("Title can't be blank", keep_on_top=True)
        return False
    elif title[1 : len(title) - 1] == issue_type:
        popup_error("Title can't be blank (only the type of issue isn't enough)", keep_on_top=True)
        return False

    if len(values['-ML DETAILS-']) < 4:
        popup_error('A little more details would be awesome', keep_on_top=True)
        return False

    return True


def _github_issue_help():
    text_font = '_ 10'

    def HelpText(text):
        return Text(text, size=(80, None), font=text_font)

    help_why = """ Let's start with a review of the Goals of the PySimpleGUI project
1. To have fun
2. For you to be successful

This form is as important as the documentation and the demo programs to meeting those goals.

The GitHub Issue GUI is here to help you more easily log issues on the PySimpleGUI GitHub Repo. """

    help_goals = """ The goals of using GitHub Issues for PySimpleGUI question, problems and suggestions are:
* Give you direct access to engineers with the most knowledge of PySimpleGUI
* Answer your questions in the most precise and correct way possible
* Provide the highest quality solutions possible
* Give you a checklist of things to try that may solve the problem
* A single, searchable database of known problems and their workarounds
* Provide a place for the PySimpleGUI project to directly provide support to users
* A list of requested enhancements
* An easy to use interface to post code and images
* A way to track the status and have converstaions about issues
* Enable multiple people to help users """

    help_explain = """ GitHub does not provide a "form" that normal bug-tracking-databases provide. As a result, a form was created specifically for the PySimpleGUI project.

The most obvious questions about this form are
* Why is there a form? Other projects don't have one?
* My question is an easy one, why does it still need a form?

The answer is:
I want you to get your question answered with the highest quality answer possible as quickly as possible.

The longer answer - For quite a while there was no form. It resulted the same back and forth, multiple questions comversation.  "What version are you running?"  "What OS are you using?"  These waste precious time.

If asking nicely helps... PLEASE ... please fill out the form.

I can assure you that this form is not here to punish you. It doesn't exist to make you angry and frustrated.  It's not here for any purpose than to try and get you support and make PySimpleGUI better. """

    help_experience = """ Not many Bug-tracking systems ask about you as a user. Your experience in programming, programming in Python and programming a GUI are asked to provide you with the best possible answer.  Here's why it's helpful.  You're a human being, with a past, and a some amount of experience.  Being able to taylor the reply to your issue in a way that fits you and your experience will result in a reply that's efficient and clear.  It's not something normally done but perhaps it should be. It's meant to provide you with a personal response.

If you've been programming for a month, the person answering your question can answer your question in a way that's understandable to you.  Similarly, if you've been programming for 20 years and have used multiple Python GUI frameworks, then you are unlikely to need as much explanation.  You'll also have a richer GUI vocabularly. It's meant to try and give you a peronally crafted response that's on your wavelength. Fun & success... Remember those are our shared goals"""

    help_steps = """ The steps to log an issue are:
1. Fill in the form
2. Click Post Issue """

    # layout = [  [T('Goals', font=heading_font, pad=(0,0))],
    #             [HelpText(help_goals)],
    #             [T('Why?', font=heading_font, pad=(0,0))],
    #             [HelpText(help_why)],
    #             [T('FAQ', font=heading_font, pad=(0,0))],
    #             [HelpText(help_explain)],
    #             [T('Experience (optional)', font=heading_font)],
    #             [HelpText(help_experience)],
    #             [T('Steps', font=heading_font, pad=(0,0))],
    #             [HelpText(help_steps)],
    #             [B('Close')]]

    t_goals = Tab('Goals', [[HelpText(help_goals)]])
    t_why = Tab('Why', [[HelpText(help_why)]])
    t_faq = Tab('FAQ', [[HelpText(help_explain)]])
    t_exp = Tab('Experience', [[HelpText(help_experience)]])
    t_steps = Tab('Steps', [[HelpText(help_steps)]])

    layout = [[TabGroup([[t_goals, t_why, t_faq, t_exp, t_steps]])], [B('Close')]]

    Window('GitHub Issue GUI Help', layout, keep_on_top=True).read(close=True)

    return


def main_open_github_issue():
    font_frame = '_ 14'
    issue_types = ('Question', 'Bug', 'Enhancement', 'Error Message')
    frame_type = [[Radio(t, 1, size=(10, 1), enable_events=True, k=t)] for t in issue_types]

    v_size = (15, 1)
    frame_versions = [
        [T('Python', size=v_size), In(sys.version, size=(20, 1), k='-VER PYTHON-')],
        [T('PySimpleGUI', size=v_size), In(ver, size=(20, 1), k='-VER PSG-')],
        [T('tkinter', size=v_size), In(tclversion_detailed, size=(20, 1), k='-VER TK-')],
    ]

    frame_platforms = [
        [T('OS                 '), T('Details')],
        [Radio('Windows', 2, running_windows(), size=(8, 1), k='-OS WIN-'), In(size=(8, 1), k='-OS WIN VER-')],
        [Radio('Linux', 2, running_linux(), size=(8, 1), k='-OS LINUX-'), In(size=(8, 1), k='-OS LINUX VER-')],
        [Radio('Mac', 2, running_mac(), size=(8, 1), k='-OS MAC-'), In(size=(8, 1), k='-OS MAC VER-')],
        [Radio('Other', 2, size=(8, 1), k='-OS OTHER-'), In(size=(8, 1), k='-OS OTHER VER-')],
    ]

    col_experience = [
        [T('Optional Experience Info')],
        [In(size=(4, 1), k='-EXP PROG-'), T('Years Programming')],
        [In(size=(4, 1), k='-EXP PYTHON-'), T('Years Writing Python')],
        [CB('Previously programmed a GUI', k='-CB PRIOR GUI-')],
        [T('Share more if you want....')],
        [In(size=(25, 1), k='-EXP NOTES-', expand_x=True)],
    ]

    checklist = (
        ('Searched main docs for your problem', 'www.PySimpleGUI.org'),
        (
            'Looked for Demo Programs that are similar to your goal.\nIt is recommend you use the Demo Browser!',
            'https://Demos.PySimpleGUI.org',
        ),
        ('If not tkinter - looked for Demo Programs for specific port', ''),
        ('For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi)', ''),
        ('Run your program outside of your debugger (from a command line)', ''),
        ('Searched through Issues (open and closed) to see if already reported', 'https://Issues.PySimpleGUI.org'),
        ('Upgraded to the latest official release of PySimpleGUI on PyPI', 'https://Upgrading.PySimpleGUI.org'),
        (
            'Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released.',
            '',
        ),
    )

    checklist_col1 = Col(
        [[CB(c, k=('-CB-', i)), T(t, k=f'-T{i}-', enable_events=True)] for i, (c, t) in enumerate(checklist[:4])],
        k='-C FRAME CBs1-',
    )
    checklist_col2 = Col(
        [[CB(c, k=('-CB-', i + 4)), T(t, k=f'-T{i + 4}-', enable_events=True)] for i, (c, t) in enumerate(checklist[4:])],
        pad=(0, 0),
        k='-C FRAME CBs2-',
    )
    checklist_tabgropup = TabGroup(
        [
            [
                Tab('Checklist 1 *', [[checklist_col1]], expand_x=True, expand_y=True),
                Tab('Checklist 2  *', [[checklist_col2]]),
                Tab('Experience', col_experience, k='-Tab Exp-', pad=(0, 0)),
            ]
        ],
        expand_x=True,
        expand_y=True,
    )

    frame_details = [[Multiline(size=(65, 10), font='Courier 10', k='-ML DETAILS-', expand_x=True, expand_y=True)]]

    tooltip_project_details = 'If you care to share a little about your project,\nthen by all means tell us what you are making!'
    frame_project_details = [
        [
            Multiline(
                size=(65, 10),
                font='Courier 10',
                k='-ML PROJECT DETAILS-',
                expand_x=True,
                expand_y=True,
                tooltip=tooltip_project_details,
            )
        ]
    ]

    tooltip_where_find_psg = 'Where did you learn about PySimpleGUI?'
    frame_where_you_found_psg = [
        [
            Multiline(
                size=(65, 10),
                font='Courier 10',
                k='-ML FOUND PSG-',
                expand_x=True,
                expand_y=True,
                tooltip=tooltip_where_find_psg,
            )
        ]
    ]

    tooltip_code = 'A short program that can be immediately run will considerably speed up getting you quality help.'
    frame_code = [[Multiline(size=(80, 10), font='Courier 8', k='-ML CODE-', expand_x=True, expand_y=True, tooltip=tooltip_code)]]

    frame_markdown = [[Multiline(size=(80, 10), font='Courier 8', k='-ML MARKDOWN-', expand_x=True, expand_y=True)]]

    top_layout = [
        [Col([[Text('Open A GitHub Issue (* = Required Info)', font='_ 15')]], expand_x=True), Col([[B('Help')]])],
        [Frame('Title *', [[Input(k='-TITLE-', size=(50, 1), font='_ 14', focus=True)]], font=font_frame)],
        # Image(data=EMOJI_BASE64_WEARY)],
        vtop(
            [
                Frame('Platform *', frame_platforms, font=font_frame),
                Frame('Type of Issue *', frame_type, font=font_frame),
                Frame('Versions *', frame_versions, font=font_frame),
            ]
        ),
    ]

    middle_layout = [
        [
            Frame(
                'Checklist * (note that you can click the links)',
                [[checklist_tabgropup]],
                font=font_frame,
                k='-CLIST FRAME-',
                expand_x=True,
                expand_y=True,
            )
        ],
        [HorizontalSeparator()],
        [
            T(
                SYMBOL_DOWN + ' If you need more room for details grab the dot and drag to expand',
                background_color='red',
                text_color='white',
            )
        ],
    ]

    bottom_layout = [
        [
            TabGroup(
                [
                    [
                        Tab('Details *\n', frame_details, pad=(0, 0)),
                        Tab('SHORT Program\nto duplicate problem *', frame_code, pad=(0, 0)),
                        Tab('Your Project Details\n(optional)', frame_project_details, pad=(0, 0)),
                        Tab('Where you found us?\n(optional)', frame_where_you_found_psg, pad=(0, 0)),
                        Tab('Markdown Output\n', frame_markdown, pad=(0, 0)),
                    ]
                ],
                k='-TABGROUP-',
                expand_x=True,
                expand_y=True,
            ),
        ]
    ]

    layout_pane = Pane([Col(middle_layout), Col(bottom_layout)], key='-PANE-', expand_x=True, expand_y=True)

    layout = [
        [
            pin(B(SYMBOL_DOWN, pad=(0, 0), k='-HIDE CLIST-', tooltip='Hide/show upper sections of window')),
            pin(Col(top_layout, k='-TOP COL-')),
        ],
        [layout_pane],
        [Col([[B('Post Issue'), B('Create Markdown Only'), B('Quit')]])],
    ]

    window = Window('Open A GitHub Issue', layout, finalize=True, resizable=True, enable_close_attempted_event=True, margins=(0, 0))

    # for i in range(len(checklist)):
    [window[f'-T{i}-'].set_cursor('hand1') for i in range(len(checklist))]

    if running_mac():
        window['-OS MAC VER-'].update(platform.mac_ver())
    elif running_windows():
        window['-OS WIN VER-'].update(platform.win32_ver())
    elif running_linux():
        window['-OS LINUX VER-'].update(platform.libc_ver())

    window.bring_to_front()
    while True:  # Event Loop
        event, values = window.read()
        # print(event, values)
        if event in (WINDOW_CLOSE_ATTEMPTED_EVENT, 'Quit'):
            if (
                popup_yes_no(
                    'Do you really want to exit?',
                    'If you have not clicked Post Issue button and then clicked "Submit New Issue" button '
                    'then your issue will not have been submitted to GitHub.\n'
                    'If you are having trouble with PySimpleGUI opening your browser, consider generating '
                    'the markdown, copying it to a text file, and then using it later to manually paste into a new issue '
                    '\n'
                    'Are you sure you want to quit?',
                    image=EMOJI_BASE64_PONDER,
                    keep_on_top=True,
                )
                == 'Yes'
            ):
                break
        if event == WIN_CLOSED:
            break
        if event in [f'-T{i}-' for i in range(len(checklist))]:
            webbrowser.open_new_tab(window[event].get())
        if event in issue_types:
            title = str(values['-TITLE-'])
            if len(title) != 0:
                if title[0] == '[' and title.find(']'):
                    title = title[title.find(']') + 1 :]
                    title = title.strip()
            window['-TITLE-'].update(f'[{event}] {title}')
        if event == '-HIDE CLIST-':
            window['-TOP COL-'].update(visible=not window['-TOP COL-'].visible)
            window['-HIDE CLIST-'].update(text=SYMBOL_UP if window['-HIDE CLIST-'].get_text() == SYMBOL_DOWN else SYMBOL_DOWN)
        if event == 'Help':
            _github_issue_help()
        elif event in ('Post Issue', 'Create Markdown Only'):
            issue_type = None
            for itype in issue_types:
                if values[itype]:
                    issue_type = itype
                    break
            if issue_type is None:
                popup_error('Must choose issue type', keep_on_top=True)
                continue
            if values['-OS WIN-']:
                operating_system = 'Windows'
                os_ver = values['-OS WIN VER-']
            elif values['-OS LINUX-']:
                operating_system = 'Linux'
                os_ver = values['-OS LINUX VER-']
            elif values['-OS MAC-']:
                operating_system = 'Mac'
                os_ver = values['-OS MAC VER-']
            elif values['-OS OTHER-']:
                operating_system = 'Other'
                os_ver = values['-OS OTHER VER-']
            else:
                popup_error('Must choose Operating System', keep_on_top=True)
                continue
            checkboxes = ['X' if values[('-CB-', i)] else ' ' for i in range(len(checklist))]

            if not _github_issue_post_validate(values, checklist, issue_types):
                continue

            cb_dict = {
                'cb_docs': checkboxes[0],
                'cb_demos': checkboxes[1],
                'cb_demo_port': checkboxes[2],
                'cb_readme_other': checkboxes[3],
                'cb_command_line': checkboxes[4],
                'cb_issues': checkboxes[5],
                'cb_latest_pypi': checkboxes[6],
                'cb_github': checkboxes[7],
                'detailed_desc': values['-ML DETAILS-'],
                'code': values['-ML CODE-'],
                'project_details': values['-ML PROJECT DETAILS-'].rstrip(),
                'where_found': values['-ML FOUND PSG-'],
            }

            markdown = _github_issue_post_make_markdown(
                issue_type,
                operating_system,
                os_ver,
                'tkinter',
                values['-VER PSG-'],
                values['-VER TK-'],
                values['-VER PYTHON-'],
                values['-EXP PYTHON-'],
                values['-EXP PROG-'],
                'Yes' if values['-CB PRIOR GUI-'] else 'No',
                values['-EXP NOTES-'],
                **cb_dict,
            )
            window['-ML MARKDOWN-'].update(markdown)
            link = _github_issue_post_make_github_link(values['-TITLE-'], window['-ML MARKDOWN-'].get())
            if event == 'Post Issue':
                webbrowser.open_new_tab(link)
            else:
                popup('Your markdown code is in the Markdown tab', keep_on_top=True)

    window.close()


def _main_entry_point():
    # print('Restarting main as a new process...(needed in case you want to GitHub Upgrade)')
    # Relaunch using the same python interpreter that was used to run this function
    interpreter = sys.executable
    if 'pythonw' in interpreter:
        interpreter = interpreter.replace('pythonw', 'python')
    execute_py_file(__file__, interpreter_command=interpreter)


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

# M"""""`'"""`YM          oo
# M  mm.  mm.  M
# M  MMM  MMM  M .d8888b. dP 88d888b.
# M  MMM  MMM  M 88'  `88 88 88'  `88
# M  MMM  MMM  M 88.  .88 88 88    88
# M  MMM  MMM  M `88888P8 dP dP    dP
# MMMMMMMMMMMMMM
#
# MM'"""""`MM            dP
# M' .mmm. `M            88
# M  MMMMMMMM .d8888b. d8888P
# M  MMM   `M 88ooood8   88
# M. `MMM' .M 88.  ...   88
# MM.     .MM `88888P'   dP
# MMMMMMMMMMM
#
# M""""""'YMM          dP
# M  mmmm. `M          88
# M  MMMMM  M .d8888b. 88d888b. dP    dP .d8888b.
# M  MMMMM  M 88ooood8 88'  `88 88    88 88'  `88
# M  MMMM' .M 88.  ... 88.  .88 88.  .88 88.  .88
# M       .MM `88888P' 88Y8888' `88888P' `8888P88
# MMMMMMMMMMM                                 .88
#                                         d8888P
# M""""""'YMM            dP
# M  mmmm. `M            88
# M  MMMMM  M .d8888b. d8888P .d8888b.
# M  MMMMM  M 88'  `88   88   88'  `88
# M  MMMM' .M 88.  .88   88   88.  .88
# M       .MM `88888P8   dP   `88888P8
# MMMMMMMMMMM


def main_get_debug_data(suppress_popup=False):
    """
    Collect up and display the data needed to file GitHub issues.
    This function will place the information on the clipboard.
    You MUST paste the information from the clipboard prior to existing your application (except on Windows).
    :param suppress_popup: If True no popup window will be shown. The string will be only returned, not displayed
    :type suppress_popup:  (bool)
    :returns:              String containing the information to place into the GitHub Issue
    :rtype:                (str)
    """
    message = get_versions()
    clipboard_set(message)

    if not suppress_popup:
        popup_scrolled(
            '*** Version information copied to your clipboard. Paste into your GitHub Issue. ***\n',
            message,
            title='Select and copy this info to your GitHub Issue',
            keep_on_top=True,
            size=(100, 10),
        )

    return message


# ..######...##........#######..########.....###....##.........
# .##....##..##.......##.....##.##.....##...##.##...##.........
# .##........##.......##.....##.##.....##..##...##..##.........
# .##...####.##.......##.....##.########..##.....##.##.........
# .##....##..##.......##.....##.##.....##.#########.##.........
# .##....##..##.......##.....##.##.....##.##.....##.##.........
# ..######...########..#######..########..##.....##.########...
# ..######..########.########.########.########.####.##....##..######....######.
# .##....##.##..........##.......##.......##.....##..###...##.##....##..##....##
# .##.......##..........##.......##.......##.....##..####..##.##........##......
# ..######..######......##.......##.......##.....##..##.##.##.##...####..######.
# .......##.##..........##.......##.......##.....##..##..####.##....##........##
# .##....##.##..........##.......##.......##.....##..##...###.##....##..##....##
# ..######..########....##.......##.......##....####.##....##..######....######.


def _global_settings_get_ttk_scrollbar_info():
    """
    This function reads the ttk scrollbar settings from the global PySimpleGUI settings file.
    Each scrollbar setting is stored with a key that's a TUPLE, not a normal string key.
    The settings are for pieces of the scrollbar and their associated piece of the PySimpleGUI theme.

    The whole ttk scrollbar feature is based on mapping parts of the scrollbar to parts of the PySimpleGUI theme.
    That is what the ttk_part_mapping_dict does, maps between the two lists of items.
    For example, the scrollbar arrow color may map to the theme input text color.

    """
    global ttk_part_mapping_dict, DEFAULT_TTK_THEME
    for ttk_part in TTK_SCROLLBAR_PART_LIST:
        value = pysimplegui_user_settings.get(json.dumps(('-ttk scroll-', ttk_part)), ttk_part_mapping_dict[ttk_part])
        ttk_part_mapping_dict[ttk_part] = value

    DEFAULT_TTK_THEME = pysimplegui_user_settings.get('-ttk theme-', DEFAULT_TTK_THEME)


def _global_settings_get_watermark_info():
    if not pysimplegui_user_settings.get('-watermark-', False) and not Window._watermark_temp_forced:
        Window._watermark = None
        return
    forced = Window._watermark_temp_forced
    prefix_text = pysimplegui_user_settings.get('-watermark text-', '')

    ver_text = ' ' + version.split(' ', 1)[0] if pysimplegui_user_settings.get('-watermark ver-', False if not forced else True) or forced else ''
    framework_ver_text = ' Tk ' + framework_version if pysimplegui_user_settings.get('-watermark framework ver-', False if not forced else True) or forced else ''
    watermark_font = pysimplegui_user_settings.get('-watermark font-', '_ 9 bold')
    # background_color = pysimplegui_user_settings.get('-watermark bg color-', 'window.BackgroundColor')
    user_text = pysimplegui_user_settings.get('-watermark text-', '')
    python_text = f' Py {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}'
    if user_text:
        text = str(user_text)
    else:
        text = prefix_text + ver_text + python_text + framework_ver_text
    Window._watermark = lambda window: Text(text, font=watermark_font, background_color=window.BackgroundColor)


def main_global_get_screen_snapshot_symcode():
    pysimplegui_user_settings = UserSettings(filename=DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME, path=DEFAULT_USER_SETTINGS_PYSIMPLEGUI_PATH)

    settings = pysimplegui_user_settings.read()

    screenshot_keysym = ''
    for i in range(4):
        keysym = settings.get(json.dumps(('-snapshot keysym-', i)), '')
        if keysym:
            screenshot_keysym += f'<{keysym}>'

    screenshot_keysym_manual = settings.get('-snapshot keysym manual-', '')

    # print('BINDING INFO!', screenshot_keysym, screenshot_keysym_manual)
    if screenshot_keysym_manual:
        return screenshot_keysym_manual
    elif screenshot_keysym:
        return screenshot_keysym
    return ''


def main_global_pysimplegui_settings_erase():
    """
    *** WARNING ***
    Deletes the PySimpleGUI settings file without asking for verification


    """
    print('********** WARNING - you are deleting your PySimpleGUI settings file **********')
    print('The file being deleted is:', pysimplegui_user_settings.full_filename)


def main_global_pysimplegui_settings():
    """
    Window to set settings that will be used across all PySimpleGUI programs that choose to use them.
    Use set_options to set the path to the folder for all PySimpleGUI settings.

    :return: True if settings were changed
    :rtype:  (bool)
    """
    global DEFAULT_WINDOW_SNAPSHOT_KEY_CODE, ttk_part_mapping_dict, DEFAULT_TTK_THEME

    key_choices = tuple(sorted(tkinter_keysyms))

    settings = pysimplegui_user_settings.read()

    editor_format_dict = {
        'pycharm': '<editor> --line <line> <file>',
        'notepad++': '<editor> -n<line> <file>',
        'sublime': '<editor> <file>:<line>',
        'vim': '<editor> +<line> <file>',
        'wing': '<editor> <file>:<line>',
        'visual studio': '<editor> <file> /command "edit.goto <line>"',
        'atom': '<editor> <file>:<line>',
        'spyder': '<editor> <file>',
        'thonny': '<editor> <file>',
        'pydev': '<editor> <file>:<line>',
        'idle': '<editor> <file>',
    }

    tooltip = (
        'Format strings for some popular editors/IDEs:\n'
        + 'PyCharm - <editor> --line <line> <file>\n'
        + 'Notepad++ - <editor> -n<line> <file>\n'
        + 'Sublime - <editor> <file>:<line>\n'
        + 'vim -  <editor> +<line> <file>\n'
        + 'wing - <editor> <file>:<line>\n'
        + 'Visual Studio - <editor> <file> /command "edit.goto <line>"\n'
        + 'Atom - <editor> <file>:<line>\n'
        + 'Spyder - <editor> <file>\n'
        + 'Thonny - <editor> <file>\n'
        + 'PyDev - <editor> <file>:<line>\n'
        + 'IDLE - <editor> <file>\n'
    )

    tooltip_file_explorer = 'This is the program you normally use to "Browse" for files\n' + 'For Windows this is normally "explorer". On Linux "nemo" is sometimes used.'

    tooltip_theme = 'The normal default theme for PySimpleGUI is "Dark Blue 13\n' + 'If you do not call theme("theme name") by your program to change the theme, then the default is used.\n' + 'This setting allows you to set the theme that PySimpleGUI will use for ALL of your programs that\n' + 'do not set a theme specifically.'

    # ------------------------- TTK Tab -------------------------
    ttk_scrollbar_tab_layout = [
        [
            T('Default TTK Theme', font='_ 16'),
            Combo([], DEFAULT_TTK_THEME, readonly=True, size=(20, 10), key='-TTK THEME-', font='_ 16'),
        ],
        [HorizontalSeparator()],
        [T('TTK Scrollbar Settings', font='_ 16')],
    ]

    t_len = max(len(item) for item in TTK_SCROLLBAR_PART_LIST)
    ttk_layout = [[]]
    for key, item in ttk_part_mapping_dict.items():
        if key in TTK_SCROLLBAR_PART_THEME_BASED_LIST:
            ttk_layout += [
                [
                    T(key, s=t_len, justification='r'),
                    Combo(
                        PSG_THEME_PART_LIST,
                        default_value=settings.get(('-ttk scroll-', key), item),
                        key=('-TTK SCROLL-', key),
                    ),
                ]
            ]
        elif key in (TTK_SCROLLBAR_PART_ARROW_WIDTH, TTK_SCROLLBAR_PART_SCROLL_WIDTH):
            ttk_layout += [
                [
                    T(key, s=t_len, justification='r'),
                    Combo(
                        list(range(100)),
                        default_value=settings.get(('-ttk scroll-', key), item),
                        key=('-TTK SCROLL-', key),
                    ),
                ]
            ]
        elif key == TTK_SCROLLBAR_PART_RELIEF:
            ttk_layout += [
                [
                    T(key, s=t_len, justification='r'),
                    Combo(
                        RELIEF_LIST,
                        default_value=settings.get(('-ttk scroll-', key), item),
                        readonly=True,
                        key=('-TTK SCROLL-', key),
                    ),
                ]
            ]

    ttk_scrollbar_tab_layout += ttk_layout
    ttk_scrollbar_tab_layout += [[Button('Reset Scrollbar Settings'), Button('Test Scrollbar Settings')]]
    ttk_tab = Tab('TTK', ttk_scrollbar_tab_layout)

    layout = [
        [
            T(
                'Global PySimpleGUI Settings',
                text_color=theme_button_color()[0],
                background_color=theme_button_color()[1],
                font='_ 18',
                expand_x=True,
                justification='c',
            )
        ]
    ]

    # ------------------------- Interpreter Tab -------------------------

    interpreter_tab = Tab(
        'Python Interpreter',
        [
            [T('Normally leave this blank')],
            [
                T('Command to run a python program:'),
                In(settings.get('-python command-', ''), k='-PYTHON COMMAND-', enable_events=True),
                FileBrowse(),
            ],
        ],
        font='_ 16',
        expand_x=True,
    )

    # ------------------------- Editor Tab -------------------------

    editor_tab = Tab(
        'Editor Settings',
        [
            [
                T('Command to invoke your editor:'),
                In(settings.get('-editor program-', ''), k='-EDITOR PROGRAM-', enable_events=True),
                FileBrowse(),
            ],
            [T('String to launch your editor to edit at a particular line #.')],
            [T('Use tags <editor> <file> <line> to specify the string')],
            [T('that will be executed to edit python files using your editor')],
            [
                T('Edit Format String (hover for tooltip)', tooltip=tooltip),
                In(settings.get('-editor format string-', '<editor> <file>'), k='-EDITOR FORMAT-', tooltip=tooltip),
            ],
        ],
        font='_ 16',
        expand_x=True,
    )

    # ------------------------- Explorer Tab -------------------------

    explorer_tab = Tab(
        'Explorer Program',
        [[In(settings.get('-explorer program-', ''), k='-EXPLORER PROGRAM-', tooltip=tooltip_file_explorer)]],
        font='_ 16',
        expand_x=True,
        tooltip=tooltip_file_explorer,
    )

    # ------------------------- Snapshots Tab -------------------------

    snapshots_tab = Tab(
        'Window Snapshots',
        [
            [
                Combo(
                    ('',) + key_choices,
                    default_value=settings.get(json.dumps(('-snapshot keysym-', i)), ''),
                    readonly=True,
                    k=('-SNAPSHOT KEYSYM-', i),
                    s=(None, 30),
                )
                for i in range(4)
            ],
            [
                T('Manually Entered Bind String:'),
                Input(settings.get('-snapshot keysym manual-', ''), k='-SNAPSHOT KEYSYM MANUAL-'),
            ],
            [
                T('Folder to store screenshots:'),
                Push(),
                In(settings.get('-screenshots folder-', ''), k='-SCREENSHOTS FOLDER-'),
                FolderBrowse(),
            ],
            [
                T('Screenshots Filename or Prefix:'),
                Push(),
                In(settings.get('-screenshots filename-', ''), k='-SCREENSHOTS FILENAME-'),
                FileBrowse(),
            ],
            [Checkbox('Auto-number Images', k='-SCREENSHOTS AUTONUMBER-')],
        ],
        font='_ 16',
        expand_x=True,
    )

    # ------------------------- Theme Tab -------------------------

    theme_tab = Tab(
        'Theme',
        [
            [T(f'Leave blank for "official" PySimpleGUI default theme: {OFFICIAL_PYSIMPLEGUI_THEME}')],
            [
                T('Default Theme For All Programs:'),
                Combo(
                    [''] + theme_list(),
                    settings.get('-theme-', None),
                    readonly=True,
                    k='-THEME-',
                    tooltip=tooltip_theme,
                ),
                Checkbox(
                    'Always use custom Titlebar',
                    default=pysimplegui_user_settings.get('-custom titlebar-', False),
                    k='-CUSTOM TITLEBAR-',
                ),
            ],
            [
                Frame(
                    'Window Watermarking',
                    [
                        [
                            Checkbox(
                                'Enable Window Watermarking',
                                pysimplegui_user_settings.get('-watermark-', False),
                                k='-WATERMARK-',
                            )
                        ],
                        [
                            T('Prefix Text String:'),
                            Input(pysimplegui_user_settings.get('-watermark text-', ''), k='-WATERMARK TEXT-'),
                        ],
                        [
                            Checkbox(
                                'PySimpleGUI Version',
                                pysimplegui_user_settings.get('-watermark ver-', False),
                                k='-WATERMARK VER-',
                            )
                        ],
                        [
                            Checkbox(
                                'Framework Version',
                                pysimplegui_user_settings.get('-watermark framework ver-', False),
                                k='-WATERMARK FRAMEWORK VER-',
                            )
                        ],
                        [
                            T('Font:'),
                            Input(pysimplegui_user_settings.get('-watermark font-', '_ 9 bold'), k='-WATERMARK FONT-'),
                        ],
                        # [T('Background Color:'), Input(pysimplegui_user_settings.get('-watermark bg color-', 'window.BackgroundColor'), k='-WATERMARK BG COLOR-')],
                    ],
                    font='_ 16',
                    expand_x=True,
                )
            ],
        ],
    )

    settings_tab_group = TabGroup(
        [
            [
                theme_tab,
                ttk_tab,
                interpreter_tab,
                explorer_tab,
                editor_tab,
                snapshots_tab,
            ]
        ]
    )
    layout += [[settings_tab_group]]
    # [T('Buttons (Leave Unchecked To Use Default) NOT YET IMPLEMENTED!',  font='_ 16')],
    #      [Checkbox('Always use TTK buttons'), CBox('Always use TK Buttons')],
    layout += [[B('Ok', bind_return_key=True), B('Cancel'), B('Mac Patch Control')]]

    window = Window('Settings', layout, keep_on_top=True, modal=False, finalize=True)

    # fill in the theme list into the Combo element - must do this AFTER the window is created or a tkinter temp window is auto created by tkinter
    ttk_theme_list = ttk.Style().theme_names()

    window['-TTK THEME-'].update(value=DEFAULT_TTK_THEME, values=ttk_theme_list)

    while True:
        event, values = window.read()
        if event in ('Cancel', WIN_CLOSED):
            break
        if event == 'Ok':
            new_theme = OFFICIAL_PYSIMPLEGUI_THEME if values['-THEME-'] == '' else values['-THEME-']
            pysimplegui_user_settings.set('-editor program-', values['-EDITOR PROGRAM-'])
            pysimplegui_user_settings.set('-explorer program-', values['-EXPLORER PROGRAM-'])
            pysimplegui_user_settings.set('-editor format string-', values['-EDITOR FORMAT-'])
            pysimplegui_user_settings.set('-python command-', values['-PYTHON COMMAND-'])
            pysimplegui_user_settings.set('-custom titlebar-', values['-CUSTOM TITLEBAR-'])
            pysimplegui_user_settings.set('-theme-', new_theme)
            pysimplegui_user_settings.set('-watermark-', values['-WATERMARK-'])
            pysimplegui_user_settings.set('-watermark text-', values['-WATERMARK TEXT-'])
            pysimplegui_user_settings.set('-watermark ver-', values['-WATERMARK VER-'])
            pysimplegui_user_settings.set('-watermark framework ver-', values['-WATERMARK FRAMEWORK VER-'])
            pysimplegui_user_settings.set('-watermark font-', values['-WATERMARK FONT-'])
            # pysimplegui_user_settings.set('-watermark bg color-', values['-WATERMARK BG COLOR-'])

            # TTK SETTINGS
            pysimplegui_user_settings.set('-ttk theme-', values['-TTK THEME-'])
            DEFAULT_TTK_THEME = values['-TTK THEME-']

            # Snapshots portion
            screenshot_keysym_manual = values['-SNAPSHOT KEYSYM MANUAL-']
            pysimplegui_user_settings.set('-snapshot keysym manual-', values['-SNAPSHOT KEYSYM MANUAL-'])
            screenshot_keysym = ''
            for i in range(4):
                pysimplegui_user_settings.set(json.dumps(('-snapshot keysym-', i)), values[('-SNAPSHOT KEYSYM-', i)])
                if values[('-SNAPSHOT KEYSYM-', i)]:
                    screenshot_keysym += '<{}>'.format(values[('-SNAPSHOT KEYSYM-', i)])
            if screenshot_keysym_manual:
                DEFAULT_WINDOW_SNAPSHOT_KEY_CODE = screenshot_keysym_manual
            elif screenshot_keysym:
                DEFAULT_WINDOW_SNAPSHOT_KEY_CODE = screenshot_keysym

            pysimplegui_user_settings.set('-screenshots folder-', values['-SCREENSHOTS FOLDER-'])
            pysimplegui_user_settings.set('-screenshots filename-', values['-SCREENSHOTS FILENAME-'])

            # TTK Scrollbar portion
            for key, value in values.items():
                if isinstance(key, tuple):
                    if key[0] == '-TTK SCROLL-':
                        pysimplegui_user_settings.set(json.dumps(('-ttk scroll-', key[1])), value)

            # Upgrade Service Settings
            pysimplegui_user_settings.set('-upgrade show only critical-', values['-UPGRADE SHOW ONLY CRITICAL-'])

            theme(new_theme)

            _global_settings_get_ttk_scrollbar_info()
            _global_settings_get_watermark_info()

            window.close()
            return True
        elif event == '-EDITOR PROGRAM-':
            for key in editor_format_dict.keys():
                if key in values['-EDITOR PROGRAM-'].lower():
                    window['-EDITOR FORMAT-'].update(value=editor_format_dict[key])
        elif event == 'Mac Patch Control':
            main_mac_feature_control()
            # re-read the settings in case they changed
            _read_mac_global_settings()
        elif event == 'Reset Scrollbar Settings':
            ttk_part_mapping_dict = copy.copy(DEFAULT_TTK_PART_MAPPING_DICT)
            for key, item in ttk_part_mapping_dict.items():
                window[('-TTK SCROLL-', key)].update(item)
        elif event == 'Test Scrollbar Settings':
            for ttk_part in TTK_SCROLLBAR_PART_LIST:
                value = values[('-TTK SCROLL-', ttk_part)]
                ttk_part_mapping_dict[ttk_part] = value
            DEFAULT_TTK_THEME = values['-TTK THEME-']
            for i in range(100):
                easy_print(i, keep_on_top=True)
            easy_print('Close this window to continue...', keep_on_top=True)

    window.close()
    # In case some of the settings were modified and tried out, reset the ttk info to be what's in the config file
    style = ttk.Style(Window.hidden_master_root)
    _change_ttk_theme(style, DEFAULT_TTK_THEME)
    _global_settings_get_ttk_scrollbar_info()

    return False


# ..######..########..##....##....##.....##.########.##.......########.
# .##....##.##.....##.##...##.....##.....##.##.......##.......##.....##
# .##.......##.....##.##..##......##.....##.##.......##.......##.....##
# ..######..##.....##.#####.......#########.######...##.......########.
# .......##.##.....##.##..##......##.....##.##.......##.......##.......
# .##....##.##.....##.##...##.....##.....##.##.......##.......##.......
# ..######..########..##....##....##.....##.########.########.##.......


def main_sdk_help():
    """
    Display a window that will display the docstrings for each PySimpleGUI Element and the Window object

    """
    online_help_links = {
        'Button': r'https://PySimpleGUI.org/en/latest/call%20reference/#button-element',
        'ButtonMenu': r'https://PySimpleGUI.org/en/latest/call%20reference/#buttonmenu-element',
        'Canvas': r'https://PySimpleGUI.org/en/latest/call%20reference/#canvas-element',
        'Checkbox': r'https://PySimpleGUI.org/en/latest/call%20reference/#checkbox-element',
        'Column': r'https://PySimpleGUI.org/en/latest/call%20reference/#column-element',
        'Combo': r'https://PySimpleGUI.org/en/latest/call%20reference/#combo-element',
        'Frame': r'https://PySimpleGUI.org/en/latest/call%20reference/#frame-element',
        'Graph': r'https://PySimpleGUI.org/en/latest/call%20reference/#graph-element',
        'HorizontalSeparator': r'https://PySimpleGUI.org/en/latest/call%20reference/#horizontalseparator-element',
        'Image': r'https://PySimpleGUI.org/en/latest/call%20reference/#image-element',
        'Input': r'https://PySimpleGUI.org/en/latest/call%20reference/#input-element',
        'Listbox': r'https://PySimpleGUI.org/en/latest/call%20reference/#listbox-element',
        'Menu': r'https://PySimpleGUI.org/en/latest/call%20reference/#menu-element',
        'MenubarCustom': r'https://PySimpleGUI.org/en/latest/call%20reference/#menubarcustom-element',
        'Multiline': r'https://PySimpleGUI.org/en/latest/call%20reference/#multiline-element',
        'OptionMenu': r'https://PySimpleGUI.org/en/latest/call%20reference/#optionmenu-element',
        'Output': r'https://PySimpleGUI.org/en/latest/call%20reference/#output-element',
        'Pane': r'https://PySimpleGUI.org/en/latest/call%20reference/#pane-element',
        'ProgressBar': r'https://PySimpleGUI.org/en/latest/call%20reference/#progressbar-element',
        'Radio': r'https://PySimpleGUI.org/en/latest/call%20reference/#radio-element',
        'Slider': r'https://PySimpleGUI.org/en/latest/call%20reference/#slider-element',
        'Spin': r'https://PySimpleGUI.org/en/latest/call%20reference/#spin-element',
        'StatusBar': r'https://PySimpleGUI.org/en/latest/call%20reference/#statusbar-element',
        'Tab': r'https://PySimpleGUI.org/en/latest/call%20reference/#tab-element',
        'TabGroup': r'https://PySimpleGUI.org/en/latest/call%20reference/#tabgroup-element',
        'Table': r'https://PySimpleGUI.org/en/latest/call%20reference/#table-element',
        'Text': r'https://PySimpleGUI.org/en/latest/call%20reference/#text-element',
        'Titlebar': r'https://PySimpleGUI.org/en/latest/call%20reference/#titlebar-element',
        'Tree': r'https://PySimpleGUI.org/en/latest/call%20reference/#tree-element',
        'VerticalSeparator': r'https://PySimpleGUI.org/en/latest/call%20reference/#verticalseparator-element',
        'Window': r'https://PySimpleGUI.org/en/latest/call%20reference/#window',
    }

    NOT_AN_ELEMENT = 'Not An Element'
    element_classes = Element.__subclasses__()
    element_names = {element.__name__: element for element in element_classes}
    element_names['Window'] = Window
    element_classes.append(Window)
    element_arg_default_dict, element_arg_default_dict_update = {}, {}
    vars3 = [m for m in inspect.getmembers(sys.modules[__name__])]

    functions = [m for m in inspect.getmembers(sys.modules[__name__], inspect.isfunction)]
    functions_names_lower = [f for f in functions if f[0][0].islower()]
    functions_names_upper = [f for f in functions if f[0][0].isupper()]
    functions_names = sorted(functions_names_lower) + sorted(functions_names_upper)

    for element in element_classes:
        # Build info about init method
        args = inspect.getfullargspec(element.__init__).args[1:]
        defaults = inspect.getfullargspec(element.__init__).defaults
        # print('------------- {element}----------')
        # print(args)
        # print(defaults)
        if len(args) != len(defaults):
            diff = len(args) - len(defaults)
            defaults = ('NO DEFAULT',) * diff + defaults
        args_defaults = []
        for i, a in enumerate(args):
            args_defaults.append((a, defaults[i]))
        element_arg_default_dict[element.__name__] = args_defaults

        # Build info about update method
        try:
            args = inspect.getfullargspec(element.update).args[1:]
            defaults = inspect.getfullargspec(element.update).defaults
            if args is None or defaults is None:
                element_arg_default_dict_update[element.__name__] = (('', ''),)
                continue
            if len(args) != len(defaults):
                diff = len(args) - len(defaults)
                defaults = ('NO DEFAULT',) * diff + defaults
            args_defaults = []
            for i, a in enumerate(args):
                args_defaults.append((a, defaults[i]))
            element_arg_default_dict_update[element.__name__] = args_defaults if len(args_defaults) else (('', ''),)
        except Exception:
            pass

    # Add on the pseudo-elements
    element_names['MenubarCustom'] = MenubarCustom
    element_names['Titlebar'] = Titlebar

    buttons = [[B(e, pad=(0, 0), size=(22, 1), font='Courier 10')] for e in sorted(element_names.keys())]
    buttons += [[B('Func Search', pad=(0, 0), size=(22, 1), font='Courier 10')]]
    button_col = Col(buttons, vertical_alignment='t')
    mline_col = Column(
        [
            [
                Multiline(
                    size=(100, 46),
                    key='-ML-',
                    write_only=True,
                    reroute_stdout=True,
                    font='Courier 10',
                    expand_x=True,
                    expand_y=True,
                )
            ],
            [T(size=(80, 1), font='Courier 10 underline', k='-DOC LINK-', enable_events=True)],
        ],
        pad=(0, 0),
        expand_x=True,
        expand_y=True,
        vertical_alignment='t',
    )
    layout = [[button_col, mline_col]]
    layout += [
        [
            CBox('Summary Only', enable_events=True, k='-SUMMARY-'),
            CBox('Display Only PEP8 Functions', default=True, k='-PEP8-'),
        ]
    ]
    # layout = [[Column(layout, scrollable=True, p=0, expand_x=True, expand_y=True, vertical_alignment='t'), Sizegrip()]]
    layout += [[Button('Exit', size=(15, 1)), Sizegrip()]]

    window = Window(
        'SDK API Call Reference',
        layout,
        resizable=True,
        use_default_focus=False,
        keep_on_top=True,
        icon=EMOJI_BASE64_THINK,
        finalize=True,
        right_click_menu=MENU_RIGHT_CLICK_EDITME_EXIT,
    )
    window['-DOC LINK-'].set_cursor('hand1')
    online_help_link = ''
    ml = window['-ML-']
    current_element = ''
    try:
        while True:  # Event Loop
            event, values = window.read()
            if event in (WIN_CLOSED, 'Exit'):
                break
            if event == '-DOC LINK-':
                if webbrowser_available and online_help_link:
                    webbrowser.open_new_tab(online_help_link)
            if event == '-SUMMARY-':
                event = current_element

            if event in element_names.keys():
                current_element = event
                window['-ML-'].update('')
                online_help_link = online_help_links.get(event, '')
                window['-DOC LINK-'].update(online_help_link)
                if not values['-SUMMARY-']:
                    elem = element_names[event]
                    ml.print(pydoc.help(elem))
                    # print the aliases for the class
                    ml.print('\n--- Shortcut Aliases for Class ---')
                    for v in vars3:
                        if elem == v[1] and elem.__name__ != v[0]:
                            print(v[0])
                    ml.print('\n--- Init Parms ---')
                else:
                    elem = element_names[event]
                    if inspect.isfunction(elem):
                        ml.print('Not a class...It is a function', background_color='red', text_color='white')
                    else:
                        element_methods = [m[0] for m in inspect.getmembers(Element, inspect.isfunction) if not m[0].startswith('_') and not m[0][0].isupper()]
                        methods = inspect.getmembers(elem, inspect.isfunction)
                        methods = [m[0] for m in methods if not m[0].startswith('_') and not m[0][0].isupper()]

                        unique_methods = [m for m in methods if m not in element_methods and not m[0][0].isupper()]

                        properties = inspect.getmembers(elem, lambda o: isinstance(o, property))
                        properties = [p[0] for p in properties if not p[0].startswith('_')]
                        ml.print('--- Methods ---', background_color='red', text_color='white')
                        ml.print('\n'.join(methods))
                        ml.print('--- Properties ---', background_color='red', text_color='white')
                        ml.print('\n'.join(properties))
                        if elem != NOT_AN_ELEMENT:
                            if issubclass(elem, Element):
                                ml.print('Methods Unique to This Element', background_color='red', text_color='white')
                                ml.print('\n'.join(unique_methods))
                        ml.print('========== Init Parms ==========', background_color='#FFFF00', text_color='black')
                        elem_text_name = event
                        for parm, default in element_arg_default_dict[elem_text_name]:
                            ml.print(f'{parm:18}', end=' = ')
                            ml.print(default, end=',\n')
                        if elem_text_name in element_arg_default_dict_update:
                            ml.print('========== Update Parms ==========', background_color='#FFFF00', text_color='black')
                            for parm, default in element_arg_default_dict_update[elem_text_name]:
                                ml.print(f'{parm:18}', end=' = ')
                                ml.print(default, end=',\n')
                ml.set_vscroll_position(0)  # scroll to top of multoline
            elif event == 'Func Search':
                search_string = popup_get_text('Search for this in function list:', keep_on_top=True)
                if search_string is not None:
                    online_help_link = ''
                    window['-DOC LINK-'].update('')
                    ml.update('')
                    for f_entry in functions_names:
                        f = f_entry[0]
                        if search_string in f.lower() and not f.startswith('_'):
                            if (values['-PEP8-'] and not f[0].isupper()) or not values['-PEP8-']:
                                if values['-SUMMARY-']:
                                    ml.print(f)
                                else:
                                    ml.print(
                                        '=========== ' + f + '===========',
                                        background_color='#FFFF00',
                                        text_color='black',
                                    )
                                    ml.print(pydoc.help(f_entry[1]))
                ml.set_vscroll_position(0)  # scroll to top of multoline
    except Exception as e:
        _error_popup_with_traceback('Exception in SDK reference', e)
    window.close()


#                     oo
#
# 88d8b.d8b. .d8888b. dP 88d888b.
# 88'`88'`88 88'  `88 88 88'  `88
# 88  88  88 88.  .88 88 88    88
# dP  dP  dP `88888P8 dP dP    dP
#
#
# M""MMM""MMM""M oo                dP
# M  MMM  MMM  M                   88
# M  MMP  MMP  M dP 88d888b. .d888b88 .d8888b. dP  dP  dP
# M  MM'  MM' .M 88 88'  `88 88'  `88 88'  `88 88  88  88
# M  `' . '' .MM 88 88    88 88.  .88 88.  .88 88.88b.88'
# M    .d  .dMMM dP dP    dP `88888P8 `88888P' 8888P Y8P
# MMMMMMMMMMMMMM
#
# MP""""""`MM   dP                       dP               dP
# M  mmmmm..M   88                       88               88
# M.      `YM d8888P .d8888b. 88d888b. d8888P .d8888b.    88d888b. .d8888b. 88d888b. .d8888b.
# MMMMMMM.  M   88   88'  `88 88'  `88   88   Y8ooooo.    88'  `88 88ooood8 88'  `88 88ooood8
# M. .MMM'  M   88   88.  .88 88         88         88    88    88 88.  ... 88       88.  ...
# Mb.     .dM   dP   `88888P8 dP         dP   `88888P'    dP    dP `88888P' dP       `88888P'
# MMMMMMMMMMM


def _main_switch_theme():
    layout = [
        [Text('Click a look and feel color to see demo window')],
        [Listbox(values=theme_list(), size=(20, 20), key='-LIST-')],
        [Button('Choose'), Button('Cancel')],
    ]

    window = Window('Change Themes', layout)

    event, values = window.read(close=True)

    if event == 'Choose':
        theme_name = values['-LIST-'][0]
        theme(theme_name)


def _create_main_window():
    """
    Creates the main test harness window.

    :return: The test window
    :rtype:  Window
    """

    tkversion = tkinter.TkVersion
    tclversion = tkinter.TclVersion
    tclversion_detailed = tkinter.Tcl().eval('info patchlevel')

    print('Starting up PySimpleGUI Diagnostic & Help System')
    print('PySimpleGUI long version = ', version)
    print(
        'PySimpleGUI Version ',
        ver,
        f'\ntcl ver = {tclversion}',
        f'tkinter version = {tkversion}',
        f'\nPython Version {sys.version}',
    )
    print(f'tcl detailed version = {tclversion_detailed}')
    print('PySimpleGUI.py location', __file__)
    # ------ Menu Definition ------ #
    menu_def = [
        ['&File', ['!&Open', '&Save::savekey', '---', '&Properties', 'E&xit']],
        [
            '&Edit',
            ['&Paste', ['Special', 'Normal', '!Disabled'], 'Undo'],
        ],
        ['&Debugger', ['Popout', 'Launch Debugger']],
        ['!&Disabled', ['Popout', 'Launch Debugger']],
        ['&Toolbar', ['Command &1', 'Command &2', 'Command &3', 'Command &4']],
        ['&Help', '&About...'],
    ]

    button_menu_def = [
        'unused',
        ['&Paste', ['Special', 'Normal', '!Disabled'], 'Undo', 'Exit'],
    ]
    treedata = TreeData()

    treedata.Insert(
        '',
        '_A_',
        'Tree Item 1',
        [1, 2, 3],
    )
    treedata.Insert(
        '',
        '_B_',
        'B',
        [4, 5, 6],
    )
    treedata.Insert(
        '_A_',
        '_A1_',
        'Sub Item 1',
        ['can', 'be', 'anything'],
    )
    treedata.Insert(
        '',
        '_C_',
        'C',
        [],
    )
    treedata.Insert(
        '_C_',
        '_C1_',
        'C1',
        ['or'],
    )
    treedata.Insert('_A_', '_A2_', 'Sub Item 2', [None, None])
    treedata.Insert('_A1_', '_A3_', 'A30', ['getting deep'])
    treedata.Insert('_C_', '_C2_', 'C2', ['nothing', 'at', 'all'])

    for i in range(100):
        treedata.Insert('_C_', i, i, [])

    frame1 = [
        [
            Input('Input Text', size=(25, 1)),
        ],
        [Multiline(size=(30, 5), default_text='Multiline Input')],
    ]

    frame2 = [
        # [ProgressBar(100, bar_color=('red', 'green'), orientation='h')],
        [
            Listbox(
                ['Listbox 1', 'Listbox 2', 'Listbox 3'],
                select_mode=SELECT_MODE_EXTENDED,
                size=(20, 5),
                no_scrollbar=True,
            ),
            Spin([1, 2, 3, 'a', 'b', 'c'], initial_value='a', size=(4, 3), wrap=True),
        ],
        [
            Combo(
                ['Combo item %s' % i for i in range(5)],
                size=(20, 3),
                default_value='Combo item 2',
                key='-COMBO1-',
            )
        ],
        [
            Combo(
                ['Combo item %s' % i for i in range(5)],
                size=(20, 3),
                font='Courier 14',
                default_value='Combo item 2',
                key='-COMBO2-',
            )
        ],
        # [Combo(['Combo item 1', 2,3,4], size=(20, 3), readonly=False, text_color='blue', background_color='red', key='-COMBO2-')],
    ]

    frame3 = [
        [Checkbox('Checkbox1', True, k='-CB1-'), Checkbox('Checkbox2', k='-CB2-')],
        [Radio('Radio Button1', 1, key='-R1-'), Radio('Radio Button2', 1, default=True, key='-R2-', tooltip='Radio 2')],
        [T('', size=(1, 4))],
    ]

    frame4 = [
        [
            Slider(range=(0, 100), orientation='v', size=(7, 15), default_value=40, key='-SLIDER1-'),
            Slider(range=(0, 100), orientation='h', size=(11, 15), default_value=40, key='-SLIDER2-'),
        ],
    ]
    matrix = [[str(x * y) for x in range(1, 5)] for y in range(1, 8)]

    frame5 = [
        vtop(
            [
                Table(
                    values=matrix,
                    headings=matrix[0],
                    auto_size_columns=False,
                    display_row_numbers=True,
                    change_submits=False,
                    justification='right',
                    header_border_width=4,
                    # header_relief=RELIEF_GROOVE,
                    num_rows=10,
                    alternating_row_color='lightblue',
                    key='-TABLE-',
                    col_widths=[5, 5, 5, 5],
                ),
                Tree(
                    data=treedata,
                    headings=['col1', 'col2', 'col3'],
                    col_widths=[5, 5, 5, 5],
                    change_submits=True,
                    auto_size_columns=False,
                    header_border_width=4,
                    # header_relief=RELIEF_GROOVE,
                    num_rows=8,
                    col0_width=8,
                    key='-TREE-',
                    show_expanded=True,
                ),
            ]
        )
    ]
    frame7 = [
        [
            Image(EMOJI_BASE64_HAPPY_HEARTS, enable_events=True, k='-EMOJI-HEARTS-'),
            T('Do you'),
            Image(HEART_3D_BASE64, subsample=3, enable_events=True, k='-HEART-'),
            T('so far?'),
        ],
        [T('Want to be taught PySimpleGUI?\nThen maybe the "Official PySimpleGUI Course" on Udemy is for you.')],
        [
            B(image_data=UDEMY_ICON, enable_events=True, k='-UDEMY-'),
            T('Check docs, announcements, easter eggs on this page for coupons.'),
        ],
        [
            B(image_data=ICON_BUY_ME_A_COFFEE, enable_events=True, k='-COFFEE-'),
            T('It is financially draining to operate a project this huge. $1 helps'),
        ],
    ]

    pop_test_tab_layout = [
        [Image(EMOJI_BASE64_HAPPY_IDEA), T('Popup tests? Good idea!')],
        [
            B('Popup', k='P '),
            B('No Titlebar', k='P NoTitle'),
            B('Not Modal', k='P NoModal'),
            B('Non Blocking', k='P NoBlock'),
            B('Auto Close', k='P AutoClose'),
        ],
        [T('"Get" popups too!')],
        [B('Get File'), B('Get Folder'), B('Get Date'), B('Get Text')],
    ]

    GRAPH_SIZE = (500, 200)
    graph_elem = Graph(GRAPH_SIZE, (0, 0), GRAPH_SIZE, key='+GRAPH+')

    frame6 = [[VPush()], [graph_elem]]

    themes_tab_layout = [
        [T('You can see a preview of the themes, the color swatches, or switch themes for this window')],
        [T('If you want to change the default theme for PySimpleGUI, use the Global Settings')],
        [B('Themes'), B('Theme Swatches'), B('Switch Themes')],
    ]

    upgrade_recommendation_tab_layout = [
        [T('Latest Recommendation and Announcements For You', font='_ 14')],
        [T('Severity Level of Update:'), T(pysimplegui_user_settings.get('-severity level-', ''))],
        [T('Recommended Version To Upgrade To:'), T(pysimplegui_user_settings.get('-upgrade recommendation-', ''))],
        [T(pysimplegui_user_settings.get('-upgrade message 1-', ''))],
        [T(pysimplegui_user_settings.get('-upgrade message 2-', ''))],
        [
            Checkbox(
                'Show Only Critical Messages',
                default=pysimplegui_user_settings.get('-upgrade show only critical-', False),
                key='-UPGRADE SHOW ONLY CRITICAL-',
                enable_events=True,
            )
        ],
        [
            Button('Show Notification Again'),
        ],
    ]
    tab_upgrade = Tab('Upgrade\n', upgrade_recommendation_tab_layout, expand_x=True)

    tab1 = Tab('Graph\n', frame6, tooltip='Graph is in here', title_color='red')
    tab2 = Tab(
        'CB, Radio\nList, Combo',
        [
            [
                Frame(
                    'Multiple Choice Group',
                    frame2,
                    title_color='#FFFFFF',
                    tooltip='Checkboxes, radio buttons, etc',
                    vertical_alignment='t',
                ),
                Frame(
                    'Binary Choice Group',
                    frame3,
                    title_color='#FFFFFF',
                    tooltip='Binary Choice',
                    vertical_alignment='t',
                ),
            ]
        ],
    )
    # tab3 = Tab('Table and Tree', [[Frame('Structured Data Group', frame5, title_color='red', element_justification='l')]], tooltip='tab 3', title_color='red', )
    tab3 = Tab(
        'Table &\nTree',
        [[Column(frame5, element_justification='l', vertical_alignment='t')]],
        tooltip='tab 3',
        title_color='red',
        k='-TAB TABLE-',
    )
    tab4 = Tab(
        'Sliders\n',
        [[Frame('Variable Choice Group', frame4, title_color='blue')]],
        tooltip='tab 4',
        title_color='red',
        k='-TAB VAR-',
    )
    tab5 = Tab(
        'Input\nMultiline',
        [[Frame('TextInput', frame1, title_color='blue')]],
        tooltip='tab 5',
        title_color='red',
        k='-TAB TEXT-',
    )
    tab6 = Tab('Course or\nSponsor', frame7, k='-TAB SPONSOR-')
    tab7 = Tab('Popups\n', pop_test_tab_layout, k='-TAB POPUP-')
    tab8 = Tab('Themes\n', themes_tab_layout, k='-TAB THEMES-')

    def VerLine(version, description, justification='r', size=(40, 1)):
        return [
            T(version, justification=justification, font='Any 12', text_color='yellow', size=size, pad=(0, 0)),
            T(description, font='Any 12', pad=(0, 0)),
        ]

    layout_top = Column(
        [
            [
                Image(EMOJI_BASE64_HAPPY_BIG_SMILE, enable_events=True, key='-LOGO-', tooltip='This is PySimpleGUI logo'),
                Image(data=DEFAULT_BASE64_LOADING_GIF, enable_events=True, key='-IMAGE-'),
                Text('PySimpleGUI Test Harness', font='ANY 14', tooltip='My tooltip', key='-TEXT1-'),
            ],
            VerLine(ver, 'PySimpleGUI Version') + [Image(HEART_3D_BASE64, subsample=4)],
            # VerLine('{}/{}'.format(tkversion, tclversion), 'TK/TCL Versions'),
            VerLine(tclversion_detailed, 'detailed tkinter version'),
            VerLine(os.path.dirname(os.path.abspath(__file__)), 'PySimpleGUI Location', size=(40, None)),
            VerLine(sys.executable, 'Python Executable'),
            VerLine(sys.version, 'Python Version', size=(40, 2)) + [Image(PYTHON_COLORED_HEARTS_BASE64, subsample=3, k='-PYTHON HEARTS-', enable_events=True)],
        ],
        pad=0,
    )

    layout_bottom = [
        [
            B(SYMBOL_DOWN, pad=(0, 0), k='-HIDE TABS-'),
            pin(
                Col(
                    [[TabGroup([[tab1, tab2, tab3, tab6, tab4, tab5, tab7, tab8, tab_upgrade]], key='-TAB_GROUP-')]],
                    k='-TAB GROUP COL-',
                )
            ),
        ],
        [
            B('Button', highlight_colors=('yellow', 'red'), pad=(1, 0)),
            B('ttk Button', use_ttk_buttons=True, tooltip='This is a TTK Button', pad=(1, 0)),
            B('See-through Mode', tooltip='Make the background transparent', pad=(1, 0)),
            B('Upgrade PySimpleGUI from GitHub', button_color='white on red', key='-INSTALL-', pad=(1, 0)),
            B('Global Settings', tooltip='Settings across all PySimpleGUI programs', pad=(1, 0)),
            B('Exit', tooltip='Exit button', pad=(1, 0)),
        ],
        # [B(image_data=ICON_BUY_ME_A_COFFEE,pad=(1, 0), key='-COFFEE-'),
        [
            B(image_data=UDEMY_ICON, pad=(1, 0), key='-UDEMY-'),
            B('SDK Reference', pad=(1, 0)),
            B('Open GitHub Issue', pad=(1, 0)),
            B('Versions for GitHub', pad=(1, 0)),
            ButtonMenu('ButtonMenu', button_menu_def, pad=(1, 0), key='-BMENU-', tearoff=True, disabled_text_color='yellow'),
        ],
    ]

    layout = [[]]

    if not theme_use_custom_titlebar():
        layout += [
            [
                Menu(
                    menu_def,
                    key='-MENU-',
                    font='Courier 15',
                    background_color='red',
                    text_color='white',
                    disabled_text_color='yellow',
                    tearoff=True,
                )
            ]
        ]
    else:
        layout += [
            [
                MenubarCustom(
                    menu_def,
                    key='-MENU-',
                    font='Courier 15',
                    bar_background_color=theme_background_color(),
                    bar_text_color=theme_text_color(),
                    background_color='red',
                    text_color='white',
                    disabled_text_color='yellow',
                )
            ]
        ]

    layout += [[layout_top] + [ProgressBar(max_value=800, size=(20, 25), orientation='v', key='+PROGRESS+')]]
    layout += layout_bottom

    window = Window(
        'PySimpleGUI Main Test Harness',
        layout,
        # font=('Helvetica', 18),
        # background_color='black',
        right_click_menu=['&Right', ['Right', 'Edit Me', '!&Click', '&Menu', 'E&xit', 'Properties']],
        # transparent_color= '#9FB8AD',
        resizable=True,
        keep_on_top=False,
        element_justification='left',  # justify contents to the left
        metadata='My window metadata',
        finalize=True,
        enable_close_attempted_event=True,
        modal=False,
    )
    window._see_through = False
    return window


# M"""""`'"""`YM          oo
# M  mm.  mm.  M
# M  MMM  MMM  M .d8888b. dP 88d888b.
# M  MMM  MMM  M 88'  `88 88 88'  `88
# M  MMM  MMM  M 88.  .88 88 88    88
# M  MMM  MMM  M `88888P8 dP dP    dP
# MMMMMMMMMMMMMM


def main():
    """
    The PySimpleGUI "Test Harness".  This is meant to be a super-quick test of the Elements.
    """
    forced_modal = DEFAULT_MODAL_WINDOWS_FORCED
    # set_options(force_modal_windows=True)
    window = _create_main_window()
    set_options(keep_on_top=True)
    graph_elem = window['+GRAPH+']
    i = 0
    graph_figures = []
    # Don't use the debug window
    # Print('', location=(0, 0), font='Courier 10', size=(100, 20), grab_anywhere=True)
    # print(window.element_list())
    while True:  # Event Loop
        event, values = window.read(timeout=5)
        if event != TIMEOUT_KEY:
            print(event, values)
            # Print(event, text_color='white', background_color='red', end='')
            # Print(values)
        if event == WIN_CLOSED or event == WIN_CLOSE_ATTEMPTED_EVENT or event == 'Exit' or (event == '-BMENU-' and values['-BMENU-'] == 'Exit'):
            break
        if i < graph_elem.CanvasSize[0]:
            x = i % graph_elem.CanvasSize[0]
            fig = graph_elem.draw_line(
                (x, 0),
                (x, random.randint(0, graph_elem.CanvasSize[1])),
                width=1,
                color=f'#{random.randint(0, 0xFFFFFF):06x}',
            )
            graph_figures.append(fig)
        else:
            x = graph_elem.CanvasSize[0]
            graph_elem.move(-1, 0)
            fig = graph_elem.draw_line(
                (x, 0),
                (x, random.randint(0, graph_elem.CanvasSize[1])),
                width=1,
                color=f'#{random.randint(0, 0xFFFFFF):06x}',
            )
            graph_figures.append(fig)
            graph_elem.delete_figure(graph_figures[0])
            del graph_figures[0]
        window['+PROGRESS+'].UpdateBar(i % 800)
        window.Element('-IMAGE-').UpdateAnimation(DEFAULT_BASE64_LOADING_GIF, time_between_frames=50)
        if event == 'Button':
            window.Element('-TEXT1-').SetTooltip('NEW TEXT')
            window.Element('-MENU-').Update(visible=True)
        elif event == 'Popout':
            show_debugger_popout_window()
        elif event == 'Launch Debugger':
            show_debugger_window()
        elif event == 'About...':
            popup(
                'About this program...',
                'You are looking at the test harness for the PySimpleGUI program',
                version,
                keep_on_top=True,
                image=DEFAULT_BASE64_ICON,
            )
        elif event.startswith('See'):
            window._see_through = not window._see_through
            window.set_transparent_color(theme_background_color() if window._see_through else '')
        elif event in ('-INSTALL-', '-UPGRADE FROM GITHUB-'):
            pass
        elif event == 'Popup':
            popup('This is your basic popup', keep_on_top=True)
        elif event == 'Get File':
            popup_scrolled('Returned:', popup_get_file('Get File', keep_on_top=True))
        elif event == 'Get Folder':
            popup_scrolled('Returned:', popup_get_folder('Get Folder', keep_on_top=True))
        elif event == 'Get Date':
            popup_scrolled('Returned:', popup_get_date(keep_on_top=True))
        elif event == 'Get Text':
            popup_scrolled('Returned:', popup_get_text('Enter some text', keep_on_top=True))
        elif event.startswith('-UDEMY-'):
            pass
        elif event.startswith('-SPONSOR-'):
            pass
        elif event == '-COFFEE-':
            pass
        elif event in ('-EMOJI-HEARTS-', '-HEART-', '-PYTHON HEARTS-'):
            pass
        elif event == 'Themes':
            search_string = popup_get_text('Enter a search term or leave blank for all themes', 'Show Available Themes', keep_on_top=True)
            if search_string is not None:
                theme_previewer(search_string=search_string)
        elif event == 'Theme Swatches':
            theme_previewer_swatches()
        elif event == 'Switch Themes':
            window.close()
            _main_switch_theme()
            window = _create_main_window()
            graph_elem = window['+GRAPH+']
        elif event == '-HIDE TABS-':
            window['-TAB GROUP COL-'].update(visible=window['-TAB GROUP COL-'].metadata is True)
            window['-TAB GROUP COL-'].metadata = not window['-TAB GROUP COL-'].metadata
            window['-HIDE TABS-'].update(text=SYMBOL_UP if window['-TAB GROUP COL-'].metadata else SYMBOL_DOWN)
        elif event == 'SDK Reference':
            main_sdk_help()
        elif event == 'Global Settings':
            if main_global_pysimplegui_settings():
                theme(pysimplegui_user_settings.get('-theme-', OFFICIAL_PYSIMPLEGUI_THEME))
                window.close()
                window = _create_main_window()
                graph_elem = window['+GRAPH+']
            else:
                Window('', layout=[[Multiline()]], alpha_channel=0).read(timeout=1, close=True)
        elif event.startswith('P '):
            if event == 'P ':
                popup('Normal Popup - Modal', keep_on_top=True)
            elif event == 'P NoTitle':
                popup_no_titlebar('No titlebar', keep_on_top=True)
            elif event == 'P NoModal':
                set_options(force_modal_windows=False)
                popup(
                    'Normal Popup - Not Modal',
                    'You can interact with main window menubar ',
                    'but will have no effect immediately',
                    'button clicks will happen after you close this popup',
                    modal=False,
                    keep_on_top=True,
                )
                set_options(force_modal_windows=forced_modal)
            elif event == 'P NoBlock':
                popup_non_blocking('Non-blocking', 'The background window should still be running', keep_on_top=True)
            elif event == 'P AutoClose':
                popup_auto_close('Will autoclose in 3 seconds', auto_close_duration=3, keep_on_top=True)
        elif event == 'Versions for GitHub':
            main_get_debug_data()
        elif event == 'Edit Me':
            execute_editor(__file__)
        elif event == 'Open GitHub Issue':
            window.minimize()
            main_open_github_issue()
            window.normal()
        elif event == 'Show Notification Again':
            pass
        elif event == '-UPGRADE SHOW ONLY CRITICAL-':
            if not running_trinket():
                pysimplegui_user_settings.set('-upgrade show only critical-', values['-UPGRADE SHOW ONLY CRITICAL-'])

        i += 1
        # _refresh_debugger()
    print('event = ', event)
    window.close()
    set_options(force_modal_windows=forced_modal)


def _optional_window_data(window):
    """
    A function to help with testing PySimpleGUI releases. Makes it easier to add a watermarked line to the bottom
    of a window while testing release candidates.

    :param window:
    :type window:       Window
    :return:            An element that will be added to the bottom of the layout
    :rtype:             None | Element
    """
    return None


pysimplegui_user_settings = UserSettings(filename=DEFAULT_USER_SETTINGS_PYSIMPLEGUI_FILENAME, path=DEFAULT_USER_SETTINGS_PYSIMPLEGUI_PATH)

# See if running on Trinket. If Trinket, then use custom titlebars since Trinket doesn't supply any
if running_trinket():
    USE_CUSTOM_TITLEBAR = True

if tclversion_detailed.startswith('8.5'):
    warnings.warn(
        'You are running a VERY old version of tkinter {}. You cannot use PNG formatted images for example.  Please upgrade to 8.6.x'.format(tclversion_detailed),
        UserWarning,
    )

# Enables the correct application icon to be shown on the Windows taskbar
if running_windows():
    try:
        myappid = 'mycompany.myproduct.subproduct.version'  # arbitrary string
        ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
    except Exception as e:
        print('Error using the taskbar icon patch', e)


_read_mac_global_settings()

if _mac_should_set_alpha_to_99():
    # Applyting Mac OS 12.3+ Alpha Channel fix.  Sets the default Alpha Channel to 0.99
    set_options(alpha_channel=0.99)


from FreeSimpleGUI.elements.base import Element
from FreeSimpleGUI.elements.button import Button
from FreeSimpleGUI.elements.button import ButtonMenu
from FreeSimpleGUI.elements.calendar import TKCalendar
from FreeSimpleGUI.elements.canvas import Canvas
from FreeSimpleGUI.elements.checkbox import Checkbox
from FreeSimpleGUI.elements.column import Column
from FreeSimpleGUI.elements.column import TkFixedFrame
from FreeSimpleGUI.elements.column import TkScrollableFrame
from FreeSimpleGUI.elements.combo import Combo
from FreeSimpleGUI.elements.error import ErrorElement
from FreeSimpleGUI.elements.frame import Frame
from FreeSimpleGUI.elements.graph import Graph
from FreeSimpleGUI.elements.image import Image
from FreeSimpleGUI.elements.input import Input
from FreeSimpleGUI.elements.list_box import Listbox
from FreeSimpleGUI.elements.menu import Menu
from FreeSimpleGUI.elements.helpers import AddMenuItem, button_color_to_tuple
from FreeSimpleGUI.elements.multiline import Multiline
from FreeSimpleGUI.elements.multiline import Output
from FreeSimpleGUI.elements.option_menu import OptionMenu
from FreeSimpleGUI.elements.pane import Pane
from FreeSimpleGUI.elements.progress_bar import ProgressBar
from FreeSimpleGUI.elements.progress_bar import TKProgressBar
from FreeSimpleGUI.elements.radio import Radio
from FreeSimpleGUI.elements.separator import HorizontalSeparator
from FreeSimpleGUI.elements.separator import VerticalSeparator
from FreeSimpleGUI.elements.sizegrip import Sizegrip
from FreeSimpleGUI.elements.slider import Slider
from FreeSimpleGUI.elements.spin import Spin
from FreeSimpleGUI.elements.status_bar import StatusBar
from FreeSimpleGUI.elements.stretch import Push
from FreeSimpleGUI.elements.stretch import VPush
from FreeSimpleGUI.elements.tab import Tab
from FreeSimpleGUI.elements.tab import TabGroup
from FreeSimpleGUI.elements.table import Table
from FreeSimpleGUI.elements.text import Text
from FreeSimpleGUI.elements.tree import Tree
from FreeSimpleGUI.elements.tree import TreeData
from FreeSimpleGUI.tray import SystemTray
from FreeSimpleGUI.window import Window
from FreeSimpleGUI._utils import _error_popup_with_traceback

# Element aliases
In = Input
InputText = Input
I = Input  # noqa
InputCombo = Combo
DropDown = InputCombo
Drop = InputCombo
DD = Combo
InputOptionMenu = OptionMenu
LBox = Listbox
LB = Listbox
R = Radio
Rad = Radio
CB = Checkbox
CBox = Checkbox
Check = Checkbox
Sp = Spin
ML = Multiline
MLine = Multiline
Txt = Text  # type: Text
T = Text  # type: Text
SBar = StatusBar
B = Button
Btn = Button
BMenu = ButtonMenu
BM = ButtonMenu
Im = Image
PBar = ProgressBar
Prog = ProgressBar
Progress = ProgressBar
G = Graph
Fr = Frame
VSeperator = VerticalSeparator
VSeparator = VerticalSeparator
VSep = VerticalSeparator
MenuBar = Menu
HSeparator = HorizontalSeparator
HSep = HorizontalSeparator
SGrip = Sizegrip
Sl = Slider
Col = Column
MenuBar = Menu
P = Push
Stretch = Push
VStretch = VPush
VP = VPush
FlexForm = Window

# additional aliases

popup_timed = popup_auto_close
test = main
sdk_help = main_sdk_help


# ------------------------ Set the "Official PySimpleGUI Theme Colors" ------------------------


theme(theme_global())
# ------------------------ Read the ttk scrollbar info ------------------------
_global_settings_get_ttk_scrollbar_info()

# ------------------------ Read the window watermark info ------------------------
_global_settings_get_watermark_info()

_DEPRECATED_NAMES = {
    'ChangeLookAndFeel': ('change_look_and_feel', change_look_and_feel),
    'ConvertArgsToSingleString': ('convert_args_to_single_string', convert_args_to_single_string),
    'EasyPrint': ('easy_print', easy_print),
    'Print': ('easy_print', easy_print),
    'eprint': ('easy_print', easy_print),
    'sgprint': ('easy_print', easy_print),
    'PrintClose': ('easy_print_close', easy_print_close),
    'sgprint_close': ('easy_print_close', easy_print_close),
    'EasyPrintClose': ('easy_print_close', easy_print_close),
    'FillFormWithValues': ('fill_form_with_values', fill_form_with_values),
    'GetComplimentaryHex': ('get_complimentary_hex', get_complimentary_hex),
    'ListOfLookAndFeelValues': ('list_of_look_and_feel_values', list_of_look_and_feel_values),
    'ObjToString': ('obj_to_string', obj_to_string),
    'ObjToStringSingleObj': ('obj_to_string_single_obj', obj_to_string_single_obj),
    'OneLineProgressMeter': ('one_line_progress_meter', one_line_progress_meter),
    'OneLineProgressMeterCancel': ('one_line_progress_meter_cancel', one_line_progress_meter_cancel),
    'Popup': ('popup', popup),
    'PopupNoFrame': ('popup_no_titlebar', popup_no_titlebar),
    'popup_no_frame': ('popup_no_titlebar', popup_no_titlebar),
    'PopupNoBorder': ('popup_no_titlebar', popup_no_titlebar),
    'popup_no_border': ('popup_no_titlebar', popup_no_titlebar),
    'PopupAnnoying': ('popup_no_titlebar', popup_no_titlebar),
    'popup_annoying': ('popup_no_titlebar', popup_no_titlebar),
    'PopupAnimated': ('popup_animated', popup_animated),
    'PopupAutoClose': ('popup_auto_close', popup_auto_close),
    'PopupCancel': ('popup_cancel', popup_cancel),
    'PopupError': ('popup_error', popup_error),
    'PopupGetFile': ('popup_get_file', popup_get_file),
    'PopupGetFolder': ('popup_get_folder', popup_get_folder),
    'PopupGetText': ('popup_get_text', popup_get_text),
    'PopupNoButtons': ('popup_no_buttons', popup_no_buttons),
    'PopupNoTitlebar': ('popup_no_titlebar', popup_no_titlebar),
    'PopupNoWait': ('popup_non_blocking', popup_non_blocking),
    'popup_no_wait': ('popup_non_blocking', popup_non_blocking),
    'PopupNonBlocking': ('popup_non_blocking', popup_non_blocking),
    'PopupOK': ('popup_ok', popup_ok),
    'PopupOKCancel': ('popup_ok_cancel', popup_ok_cancel),
    'PopupQuick': ('popup_quick', popup_quick),
    'PopupQuickMessage': ('popup_quick_message', popup_quick_message),
    'PopupScrolled': ('popup_scrolled', popup_scrolled),
    'PopupTimed': ('popup_timed', popup_auto_close),
    'PopupYesNo': ('popup_yes_no', popup_yes_no),
    'RGB': ('rgb', rgb),
    'SetGlobalIcon': ('set_global_icon', set_global_icon),
    'SetOptions': ('set_options', set_options),
    'sprint': ('popup_scrolled', popup_scrolled),
    'ScrolledTextBox': ('popup_scrolled', popup_scrolled),
    'TimerStart': ('timer_start', timer_start),
    'TimerStop': ('timer_stop', timer_stop),
}


def __getattr__(name):
    if name in ('pil_import_attempted', 'pil_imported'):
        warnings.warn(f'The name {name} is deprecated. This value will always be False. In a future version, this will become an AttributeError.', DeprecationWarning, stacklevel=2)
        return False
    elif name in _DEPRECATED_NAMES:
        new_name, ret = _DEPRECATED_NAMES[name]
        warnings.warn(f'{name} is deprecated. Use {new_name} instead', DeprecationWarning, stacklevel=2)
        return ret
    raise AttributeError(f'module {__name__} has no attribute {name!r}')


# -------------------------------- ENTRY POINT IF RUN STANDALONE -------------------------------- #
if __name__ == '__main__':
    # To execute the upgrade from command line, type:
    # python -m PySimpleGUI.PySimpleGUI upgrade
    if len(sys.argv) > 1 and sys.argv[1] == 'upgrade':
        print(
            'Upgrading FreeSimpleGUI in place is not supported. Please use pip or your preferred package manager to update FreeSimpleGUI.',
            file=sys.stderr,
        )
        exit(1)
    elif len(sys.argv) > 1 and sys.argv[1] == 'help':
        main_sdk_help()
        exit(0)
    main()
    exit(0)
