2018-07-11 19:19:24 +00:00
#!/usr/bin/env Python3
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
import tkinter . scrolledtext as tkst
import tkinter . font
import datetime
import sys
import textwrap
2018-08-06 16:20:18 +00:00
# ----====----====----==== Constants the user CAN safely change ====----====----====----#
2018-07-11 19:19:24 +00:00
DEFAULT_WINDOW_ICON = ' '
DEFAULT_ELEMENT_SIZE = ( 45 , 1 ) # In CHARACTERS
2018-08-28 16:25:15 +00:00
DEFAULT_BUTTON_ELEMENT_SIZE = ( 10 , 1 ) # In CHARACTERS
2018-07-11 19:19:24 +00:00
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
2018-08-12 21:45:12 +00:00
DEFAULT_AUTOSIZE_TEXT = True
2018-07-27 19:45:38 +00:00
DEFAULT_AUTOSIZE_BUTTONS = True
2018-07-11 19:19:24 +00:00
DEFAULT_FONT = ( " Helvetica " , 10 )
2018-07-21 00:07:04 +00:00
DEFAULT_TEXT_JUSTIFICATION = ' left '
2018-07-23 19:14:34 +00:00
DEFAULT_BORDER_WIDTH = 1
2018-07-11 19:19:24 +00:00
DEFAULT_AUTOCLOSE_TIME = 3 # time in seconds to show an autoclose form
2018-07-21 00:07:04 +00:00
DEFAULT_DEBUG_WINDOW_SIZE = ( 80 , 20 )
2018-07-30 21:00:42 +00:00
DEFAULT_WINDOW_LOCATION = ( None , None )
2018-07-11 19:19:24 +00:00
MAX_SCROLLED_TEXT_BOX_HEIGHT = 50
#################### 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 ] ) )
2018-07-26 23:53:17 +00:00
COLOR_SYSTEM_DEFAULT = ' 1234567890 ' # Colors should never be this long
2018-08-29 00:07:17 +00:00
if sys . platform is ' darwin ' :
DEFAULT_BUTTON_COLOR = COLOR_SYSTEM_DEFAULT # Foreground, Background (None, None) == System Default
else :
DEFAULT_BUTTON_COLOR = ( ' white ' , BLUES [ 0 ] ) # Foreground, Background (None, None) == System Default
2018-07-26 23:53:17 +00:00
DEFAULT_ERROR_BUTTON_COLOR = ( " #FFFFFF " , " #FF0000 " )
DEFAULT_BACKGROUND_COLOR = None
DEFAULT_ELEMENT_BACKGROUND_COLOR = None
2018-08-07 11:30:51 +00:00
DEFAULT_ELEMENT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
2018-07-26 23:53:17 +00:00
DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR = None
2018-08-07 11:30:51 +00:00
DEFAULT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
2018-07-26 23:53:17 +00:00
DEFAULT_INPUT_ELEMENTS_COLOR = COLOR_SYSTEM_DEFAULT
2018-08-16 13:21:30 +00:00
DEFAULT_INPUT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
2018-07-26 23:53:17 +00:00
DEFAULT_SCROLLBAR_COLOR = None
2018-07-11 19:19:24 +00:00
# DEFAULT_BUTTON_COLOR = (YELLOWS[0], PURPLES[0]) # (Text, Background) or (Color "on", Color) as a way to remember
# DEFAULT_BUTTON_COLOR = (GREENS[3], TANS[0]) # Foreground, Background (None, None) == System Default
2018-07-13 14:53:50 +00:00
# DEFAULT_BUTTON_COLOR = (YELLOWS[0], GREENS[4]) # Foreground, Background (None, None) == System Default
2018-07-23 19:14:34 +00:00
# DEFAULT_BUTTON_COLOR = ('white', 'black') # Foreground, Background (None, None) == System Default
2018-07-11 19:19:24 +00:00
# DEFAULT_BUTTON_COLOR = (YELLOWS[0], PURPLES[2]) # Foreground, Background (None, None) == System Default
# DEFAULT_PROGRESS_BAR_COLOR = (GREENS[2], GREENS[0]) # a nice green progress bar
# DEFAULT_PROGRESS_BAR_COLOR = (BLUES[1], BLUES[1]) # a nice green progress bar
# DEFAULT_PROGRESS_BAR_COLOR = (BLUES[0], BLUES[0]) # a nice green progress bar
# DEFAULT_PROGRESS_BAR_COLOR = (PURPLES[1],PURPLES[0]) # a nice purple progress bar
2018-07-24 18:12:39 +00:00
# A transparent button is simply one that matches the background
TRANSPARENT_BUTTON = ( ' #F0F0F0 ' , ' #F0F0F0 ' )
#--------------------------------------------------------------------------------
2018-07-26 23:53:17 +00:00
# Progress Bar Relief Choices
RELIEF_RAISED = ' raised '
RELIEF_SUNKEN = ' sunken '
RELIEF_FLAT = ' flat '
RELIEF_RIDGE = ' ridge '
RELIEF_GROOVE = ' groove '
RELIEF_SOLID = ' solid '
2018-07-24 18:12:39 +00:00
2018-07-26 23:53:17 +00:00
DEFAULT_PROGRESS_BAR_COLOR = ( GREENS [ 0 ] , ' #D0D0D0 ' ) # a nice green progress bar
2018-07-27 19:45:38 +00:00
DEFAULT_PROGRESS_BAR_SIZE = ( 25 , 20 ) # Size of Progress Bar (characters for length, pixels for width)
2018-07-23 19:14:34 +00:00
DEFAULT_PROGRESS_BAR_BORDER_WIDTH = 1
2018-07-26 23:53:17 +00:00
DEFAULT_PROGRESS_BAR_RELIEF = RELIEF_GROOVE
PROGRESS_BAR_STYLES = ( ' default ' , ' winnative ' , ' clam ' , ' alt ' , ' classic ' , ' vista ' , ' xpnative ' )
2018-07-11 19:19:24 +00:00
DEFAULT_PROGRESS_BAR_STYLE = ' default '
DEFAULT_METER_ORIENTATION = ' Horizontal '
2018-07-23 19:14:34 +00:00
DEFAULT_SLIDER_ORIENTATION = ' vertical '
DEFAULT_SLIDER_BORDER_WIDTH = 1
2018-08-07 11:30:51 +00:00
DEFAULT_SLIDER_RELIEF = tk . FLAT
2018-07-23 19:14:34 +00:00
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 '
2018-07-11 19:19:24 +00:00
# DEFAULT_METER_ORIENTATION = 'Vertical'
# ----====----====----==== Constants the user should NOT f-with ====----====----====----#
ThisRow = 555666777 # magic number
2018-07-26 23:53:17 +00:00
2018-07-11 19:19:24 +00:00
# DEFAULT_WINDOW_ICON = ''
MESSAGE_BOX_LINE_WIDTH = 60
# a shameful global variable. This represents the top-level window information. Needed because opening a second window is different than opening the first.
class MyWindows ( ) :
def __init__ ( self ) :
self . NumOpenWindows = 0
self . user_defined_icon = None
2018-08-18 20:55:21 +00:00
def Decrement ( self ) :
self . NumOpenWindows - = 1 * ( self . NumOpenWindows != 0 ) # decrement if not 0
2018-08-20 17:48:02 +00:00
# print('---- DECREMENTING Num Open Windows = {} ---'.format(self.NumOpenWindows))
2018-08-18 20:55:21 +00:00
def Increment ( self ) :
self . NumOpenWindows + = 1
2018-08-20 17:48:02 +00:00
# print('++++ INCREMENTING Num Open Windows = {} ++++'.format(self.NumOpenWindows))
2018-08-18 20:55:21 +00:00
2018-07-11 19:19:24 +00:00
_my_windows = MyWindows ( ) # terrible hack using globals... means need a class for collecing windows
# ====================================================================== #
# One-liner functions that are handy as f_ck #
# ====================================================================== #
def RGB ( red , green , blue ) : return ' # %02x %02x %02x ' % ( red , green , blue )
# ====================================================================== #
# Enums for types #
# ====================================================================== #
# ------------------------- Button types ------------------------- #
#todo Consider removing the Submit, Cancel types... they are just 'RETURN' type in reality
#uncomment this line and indent to go back to using Enums
# class ButtonType(Enum):
2018-07-31 03:50:52 +00:00
BUTTON_TYPE_BROWSE_FOLDER = 1
BUTTON_TYPE_BROWSE_FILE = 2
2018-08-27 13:56:35 +00:00
BUTTON_TYPE_BROWSE_FILES = 21
2018-08-18 20:55:21 +00:00
BUTTON_TYPE_SAVEAS_FILE = 3
2018-07-31 03:50:52 +00:00
BUTTON_TYPE_CLOSES_WIN = 5
BUTTON_TYPE_READ_FORM = 7
BUTTON_TYPE_REALTIME = 9
2018-07-11 19:19:24 +00:00
# ------------------------- Element types ------------------------- #
# class ElementType(Enum):
2018-07-21 00:07:04 +00:00
ELEM_TYPE_TEXT = 1
ELEM_TYPE_INPUT_TEXT = 20
ELEM_TYPE_INPUT_COMBO = 21
ELEM_TYPE_INPUT_RADIO = 5
ELEM_TYPE_INPUT_MULTILINE = 7
ELEM_TYPE_INPUT_CHECKBOX = 8
ELEM_TYPE_INPUT_SPIN = 9
ELEM_TYPE_BUTTON = 3
ELEM_TYPE_IMAGE = 30
2018-08-26 19:20:54 +00:00
ELEM_TYPE_CANVAS = 40
2018-08-27 02:16:54 +00:00
ELEM_TYPE_FRAME = 41
2018-07-23 19:14:34 +00:00
ELEM_TYPE_INPUT_SLIDER = 10
ELEM_TYPE_INPUT_LISTBOX = 11
2018-07-21 00:07:04 +00:00
ELEM_TYPE_OUTPUT = 300
2018-08-14 20:49:36 +00:00
ELEM_TYPE_COLUMN = 555
2018-07-21 00:07:04 +00:00
ELEM_TYPE_PROGRESS_BAR = 200
ELEM_TYPE_BLANK = 100
2018-07-11 19:19:24 +00:00
# ------------------------- MsgBox Buttons Types ------------------------- #
MSG_BOX_YES_NO = 1
MSG_BOX_CANCELLED = 2
MSG_BOX_ERROR = 3
MSG_BOX_OK_CANCEL = 4
MSG_BOX_OK = 0
# ---------------------------------------------------------------------- #
# Cascading structure.... Objects get larger #
# Button #
# Element #
# Row #
# Form #
# ---------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
# Element CLASS #
# ------------------------------------------------------------------------- #
class Element ( ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , type , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , font = None , background_color = None , text_color = None , key = None , pad = None ) :
2018-07-16 18:52:16 +00:00
self . Size = size
self . Type = type
self . AutoSizeText = auto_size_text
self . Scale = scale
2018-08-26 02:57:15 +00:00
self . Pad = DEFAULT_ELEMENT_PADDING if pad is None else pad
2018-07-16 18:52:16 +00:00
self . Font = font
2018-07-11 19:19:24 +00:00
self . TKStringVar = None
self . TKIntVar = None
self . TKText = None
self . TKEntry = None
2018-07-21 00:07:04 +00:00
self . TKImage = None
2018-07-11 19:19:24 +00:00
self . ParentForm = None
self . TextInputDefault = None
self . Position = ( 0 , 0 ) # Default position Row 0, Col 0
2018-07-26 23:53:17 +00:00
self . BackgroundColor = background_color if background_color is not None else DEFAULT_ELEMENT_BACKGROUND_COLOR
2018-08-07 11:30:51 +00:00
self . TextColor = text_color if text_color is not None else DEFAULT_ELEMENT_TEXT_COLOR
2018-08-08 14:40:40 +00:00
self . Key = key # dictionary key for return values
2018-07-11 19:19:24 +00:00
2018-08-28 03:57:52 +00:00
def FindReturnKeyBoundButton ( self , form ) :
for row in form . Rows :
for element in row :
if element . Type == ELEM_TYPE_BUTTON :
if element . BindReturnKey :
return element
if element . Type == ELEM_TYPE_COLUMN :
rc = self . FindReturnKeyBoundButton ( element )
if rc is not None :
return rc
return None
2018-08-09 13:35:21 +00:00
def ReturnKeyHandler ( self , event ) :
MyForm = self . ParentForm
2018-08-28 03:57:52 +00:00
button_element = self . FindReturnKeyBoundButton ( MyForm )
if button_element is not None :
button_element . ButtonCallBack ( )
def ListboxSelectHandler ( self , event ) :
MyForm = self . ParentForm
2018-08-28 20:42:51 +00:00
# first, get the results table built
# modify the Results table in the parent FlexForm object
self . ParentForm . LastButtonClicked = ' '
self . ParentForm . FormRemainedOpen = True
self . ParentForm . TKroot . quit ( ) # kick the users out of the mainloop
2018-08-09 13:35:21 +00:00
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
try :
self . TKStringVar . __del__ ( )
except :
pass
try :
self . TKIntVar . __del__ ( )
except :
pass
try :
self . TKText . __del__ ( )
except :
pass
try :
self . TKEntry . __del__ ( )
except :
pass
# ---------------------------------------------------------------------- #
2018-07-18 02:08:11 +00:00
# Input Class #
2018-07-11 19:19:24 +00:00
# ---------------------------------------------------------------------- #
class InputText ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , default_text = ' ' , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , password_char = ' ' , background_color = None , text_color = None , do_not_clear = False , key = None , focus = False , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Input a line of text Element
: param default_text : Default value to display
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param password_char : If non - blank , will display this character for every character typed
: param background_color : Color for Element . Text or RGB Hex
'''
2018-07-16 18:52:16 +00:00
self . DefaultText = default_text
2018-07-18 02:08:11 +00:00
self . PasswordCharacter = password_char
2018-08-16 13:21:30 +00:00
bg = background_color if background_color is not None else DEFAULT_INPUT_ELEMENTS_COLOR
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
2018-08-09 13:35:21 +00:00
self . Focus = focus
2018-08-10 13:15:19 +00:00
self . do_not_clear = do_not_clear
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_TEXT , scale = scale , size = size , auto_size_text = auto_size_text , background_color = bg , text_color = fg , key = key , pad = pad )
2018-07-11 19:19:24 +00:00
2018-08-09 13:35:21 +00:00
2018-08-21 01:59:00 +00:00
def Update ( self , new_value ) :
self . TKStringVar . set ( new_value )
2018-07-18 02:08:11 +00:00
2018-08-22 15:37:51 +00:00
def Get ( self ) :
return self . TKStringVar . get ( )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# Combo #
# ---------------------------------------------------------------------- #
class InputCombo ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , values , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , background_color = None , text_color = None , key = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Input Combo Box Element ( also called Dropdown box )
: param values :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param background_color : Color for Element . Text or RGB Hex
'''
2018-07-16 18:52:16 +00:00
self . Values = values
2018-07-11 19:19:24 +00:00
self . TKComboBox = None
2018-07-26 23:53:17 +00:00
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
2018-08-16 13:21:30 +00:00
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_COMBO , scale = scale , size = size , auto_size_text = auto_size_text , background_color = bg , text_color = fg , key = key , pad = pad )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
try :
self . TKComboBox . __del__ ( )
except :
pass
super ( ) . __del__ ( )
2018-07-23 19:14:34 +00:00
# ---------------------------------------------------------------------- #
2018-08-18 20:55:21 +00:00
# Listbox #
2018-07-23 19:14:34 +00:00
# ---------------------------------------------------------------------- #
class Listbox ( Element ) :
2018-08-28 03:57:52 +00:00
def __init__ ( self , values , select_mode = None , select_submits = False , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , font = None , background_color = None , text_color = None , key = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Listbox Element
: param values :
2018-08-18 20:52:24 +00:00
: param select_mode : SELECT_MODE_BROWSE , SELECT_MODE_EXTENDED , SELECT_MODE_MULTIPLE , SELECT_MODE_SINGLE
2018-07-28 18:41:56 +00:00
: param font :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param background_color : Color for Element . Text or RGB Hex '''
2018-07-23 19:14:34 +00:00
self . Values = values
2018-08-23 16:30:35 +00:00
self . TKListbox = None
2018-08-28 03:57:52 +00:00
self . SelectSubmits = select_submits
2018-07-23 19:14:34 +00:00
if select_mode == LISTBOX_SELECT_MODE_BROWSE :
self . SelectMode = SELECT_MODE_BROWSE
elif select_mode == LISTBOX_SELECT_MODE_EXTENDED :
self . SelectMode = SELECT_MODE_EXTENDED
elif select_mode == LISTBOX_SELECT_MODE_MULTIPLE :
self . SelectMode = SELECT_MODE_MULTIPLE
elif select_mode == LISTBOX_SELECT_MODE_SINGLE :
self . SelectMode = SELECT_MODE_SINGLE
else :
self . SelectMode = DEFAULT_LISTBOX_SELECT_MODE
2018-07-26 23:53:17 +00:00
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
2018-08-16 13:21:30 +00:00
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
2018-08-28 03:57:52 +00:00
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_LISTBOX , scale = scale , size = size , auto_size_text = auto_size_text , font = font , background_color = bg , text_color = fg , key = key , pad = pad )
2018-07-23 19:14:34 +00:00
2018-08-23 16:30:35 +00:00
def Update ( self , values ) :
self . TKListbox . delete ( 0 , ' end ' )
for item in values :
self . TKListbox . insert ( tk . END , item )
self . TKListbox . selection_set ( 0 , 0 )
2018-07-23 19:14:34 +00:00
def __del__ ( self ) :
try :
self . TKListBox . __del__ ( )
except :
pass
super ( ) . __del__ ( )
2018-07-11 19:19:24 +00:00
# ---------------------------------------------------------------------- #
# Radio #
# ---------------------------------------------------------------------- #
class Radio ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , text , group_id , default = False , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , background_color = None , text_color = None , font = None , key = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Radio Button Element
: param text :
: param group_id :
: param default :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param background_color : Color for Element . Text or RGB Hex
: param font :
'''
2018-07-16 18:52:16 +00:00
self . InitialState = default
self . Text = text
2018-07-11 19:19:24 +00:00
self . TKRadio = None
2018-07-16 18:52:16 +00:00
self . GroupID = group_id
2018-07-11 19:19:24 +00:00
self . Value = None
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_RADIO , scale = scale , size = size , auto_size_text = auto_size_text , font = font , background_color = background_color , text_color = text_color , key = key , pad = pad )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
try :
self . TKRadio . __del__ ( )
except :
pass
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# Checkbox #
# ---------------------------------------------------------------------- #
class Checkbox ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , text , default = False , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , font = None , background_color = None , text_color = None , key = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Check Box Element
: param text :
: param default :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param background_color : Color for Element . Text or RGB Hex
: param font :
'''
2018-07-16 18:52:16 +00:00
self . Text = text
self . InitialState = default
2018-07-11 19:19:24 +00:00
self . Value = None
2018-08-22 15:37:51 +00:00
self . TKCheckbutton = None
2018-07-11 19:19:24 +00:00
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_CHECKBOX , scale = scale , size = size , auto_size_text = auto_size_text , font = font , background_color = background_color , text_color = text_color , key = key , pad = pad )
2018-07-11 19:19:24 +00:00
2018-08-22 15:37:51 +00:00
def Get ( self ) :
return self . TKIntVar . get ( )
def Update ( self , value ) :
if value is None :
self . TKCheckbutton . configure ( state = ' disabled ' )
else :
self . TKCheckbutton . configure ( state = ' normal ' )
self . TKIntVar . set ( value )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# Spin #
# ---------------------------------------------------------------------- #
class Spin ( Element ) :
# Values = None
# TKSpinBox = None
2018-08-26 02:57:15 +00:00
def __init__ ( self , values , initial_value = None , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , font = None , background_color = None , text_color = None , key = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Spin Box Element
: param values :
: param initial_value :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param background_color : Color for Element . Text or RGB Hex
: param font :
'''
2018-07-16 18:52:16 +00:00
self . Values = values
self . DefaultValue = initial_value
2018-07-11 19:19:24 +00:00
self . TKSpinBox = None
2018-07-26 23:53:17 +00:00
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
2018-08-16 13:21:30 +00:00
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_SPIN , scale , size , auto_size_text , font = font , background_color = bg , text_color = fg , key = key , pad = pad )
2018-07-11 19:19:24 +00:00
return
def __del__ ( self ) :
try :
self . TKSpinBox . __del__ ( )
except :
pass
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# Multiline #
# ---------------------------------------------------------------------- #
class Multiline ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , default_text = ' ' , enter_submits = False , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , background_color = None , text_color = None , do_not_clear = False , key = None , focus = False , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Input Multi - line Element
: param default_text :
: param enter_submits :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param background_color : Color for Element . Text or RGB Hex
'''
2018-07-16 18:52:16 +00:00
self . DefaultText = default_text
self . EnterSubmits = enter_submits
2018-07-26 23:53:17 +00:00
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
2018-08-09 13:35:21 +00:00
self . Focus = focus
2018-08-10 13:15:19 +00:00
self . do_not_clear = do_not_clear
2018-08-16 13:21:30 +00:00
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_MULTILINE , scale = scale , size = size , auto_size_text = auto_size_text , background_color = bg , text_color = fg , key = key , pad = pad )
2018-07-11 19:19:24 +00:00
return
2018-08-09 17:00:50 +00:00
def Update ( self , NewValue ) :
2018-08-29 01:05:11 +00:00
self . TKText . delete ( ' 1.0 ' , tk . END )
2018-08-09 17:00:50 +00:00
self . TKText . insert ( 1.0 , NewValue )
2018-07-11 19:19:24 +00:00
2018-08-22 15:37:51 +00:00
def Get ( self ) :
return self . TKText . get ( 1.0 , tk . END )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# Text #
# ---------------------------------------------------------------------- #
class Text ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , text , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , font = None , text_color = None , background_color = None , justification = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Text Element - Displays text in your form . Can be updated in non - blocking forms
: param text : The text to display
: param scale : Scaling factor ( w , h ) ( 2 , 2 ) = 2 * Size
: param size : Size of Element in Characters
: param auto_size_text : True if the field should shrink to fit the text
: param font : Font name and size ( " name " , size )
: param text_color : Text Color name or RGB hex value ' #RRGGBB '
: param background_color : Background color for text ( name or RGB Hex )
: param justification : ' left ' , ' right ' , ' center '
'''
2018-07-16 18:52:16 +00:00
self . DisplayText = text
2018-07-26 23:53:17 +00:00
self . TextColor = text_color if text_color else DEFAULT_TEXT_COLOR
2018-08-21 22:29:32 +00:00
self . Justification = justification
2018-07-26 23:53:17 +00:00
if background_color is None :
bg = DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR
else :
bg = background_color
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_TEXT , scale , size , auto_size_text , background_color = bg , font = font if font else DEFAULT_FONT , text_color = self . TextColor , pad = pad )
2018-07-11 19:19:24 +00:00
return
2018-08-23 16:45:51 +00:00
def Update ( self , new_value = None , background_color = None , text_color = None ) :
if new_value is not None :
self . DisplayText = new_value
stringvar = self . TKStringVar
stringvar . set ( new_value )
if background_color is not None :
self . TKText . configure ( background = background_color )
if text_color is not None :
self . TKText . configure ( fg = text_color )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# TKProgressBar #
# Emulate the TK ProgressBar using canvas and rectangles
# ---------------------------------------------------------------------- #
class TKProgressBar ( ) :
2018-07-27 19:45:38 +00:00
def __init__ ( self , root , max , length = 400 , width = DEFAULT_PROGRESS_BAR_SIZE [ 1 ] , style = DEFAULT_PROGRESS_BAR_STYLE , relief = DEFAULT_PROGRESS_BAR_RELIEF , border_width = DEFAULT_PROGRESS_BAR_BORDER_WIDTH , orientation = ' horizontal ' , BarColor = ( None , None ) ) :
2018-07-16 18:52:16 +00:00
self . Length = length
self . Width = width
self . Max = max
self . Orientation = orientation
2018-07-11 19:19:24 +00:00
self . Count = None
self . PriorCount = 0
2018-08-27 02:16:54 +00:00
2018-07-16 18:52:16 +00:00
if orientation [ 0 ] . lower ( ) == ' h ' :
2018-07-26 23:53:17 +00:00
s = ttk . Style ( )
s . theme_use ( style )
2018-08-25 02:47:21 +00:00
s . configure ( str ( length ) + str ( width ) + " my.Horizontal.TProgressbar " , background = BarColor [ 0 ] , troughcolor = BarColor [ 1 ] , troughrelief = relief , borderwidth = border_width , thickness = width )
self . TKProgressBarForReal = ttk . Progressbar ( root , maximum = self . Max , style = str ( length ) + str ( width ) + ' my.Horizontal.TProgressbar ' , length = length , orient = tk . HORIZONTAL , mode = ' determinate ' )
2018-07-11 19:19:24 +00:00
else :
2018-07-26 23:53:17 +00:00
s = ttk . Style ( )
s . theme_use ( style )
2018-08-25 02:47:21 +00:00
s . configure ( str ( length ) + str ( width ) + " my.Vertical.TProgressbar " , background = BarColor [ 0 ] , troughcolor = BarColor [ 1 ] , troughrelief = relief , borderwidth = border_width , thickness = width )
self . TKProgressBarForReal = ttk . Progressbar ( root , maximum = self . Max , style = str ( length ) + str ( width ) + ' my.Vertical.TProgressbar ' , length = length , orient = tk . VERTICAL , mode = ' determinate ' )
2018-07-11 19:19:24 +00:00
2018-08-25 02:47:21 +00:00
def Update ( self , count , max = None ) :
if max is not None :
self . Max = max
try :
self . TKProgressBarForReal . config ( maximum = max )
except :
return False
2018-07-26 23:53:17 +00:00
if count > self . Max : return False
try :
self . TKProgressBarForReal [ ' value ' ] = count
except : return False
2018-07-11 19:19:24 +00:00
return True
def __del__ ( self ) :
try :
2018-07-26 23:53:17 +00:00
self . TKProgressBarForReal . __del__ ( )
2018-07-11 19:19:24 +00:00
except :
pass
# ---------------------------------------------------------------------- #
2018-07-27 19:45:38 +00:00
# TKOutput #
# New Type of TK Widget that's a Text Widget in disguise #
# Note that it's inherited from the TKFrame class so that the #
2018-08-22 15:37:51 +00:00
# Scroll bar will span the length of the frame #
2018-07-11 19:19:24 +00:00
# ---------------------------------------------------------------------- #
class TKOutput ( tk . Frame ) :
2018-08-28 16:25:15 +00:00
def __init__ ( self , parent , width , height , bd , background_color = None , text_color = None , font = None , pad = None ) :
2018-08-26 19:20:54 +00:00
frame = tk . Frame ( parent )
2018-08-18 20:55:21 +00:00
tk . Frame . __init__ ( self , frame )
2018-08-26 19:20:54 +00:00
self . output = tk . Text ( frame , width = width , height = height , bd = bd , font = font )
2018-07-26 23:53:17 +00:00
if background_color and background_color != COLOR_SYSTEM_DEFAULT :
self . output . configure ( background = background_color )
2018-08-26 19:20:54 +00:00
frame . configure ( background = background_color )
2018-08-07 11:30:51 +00:00
if text_color and text_color != COLOR_SYSTEM_DEFAULT :
self . output . configure ( fg = text_color )
2018-08-18 20:55:21 +00:00
self . vsb = tk . Scrollbar ( frame , orient = " vertical " , command = self . output . yview )
2018-07-11 19:19:24 +00:00
self . output . configure ( yscrollcommand = self . vsb . set )
2018-08-18 20:55:21 +00:00
self . output . pack ( side = " left " , fill = " both " )
2018-07-26 23:53:17 +00:00
self . vsb . pack ( side = " left " , fill = " y " )
2018-08-28 16:25:15 +00:00
frame . pack ( side = " left " , padx = pad [ 0 ] , pady = pad [ 1 ] )
2018-07-11 19:19:24 +00:00
self . previous_stdout = sys . stdout
self . previous_stderr = sys . stderr
sys . stdout = self
sys . stderr = self
self . pack ( )
def write ( self , txt ) :
try :
self . output . insert ( tk . END , str ( txt ) )
self . output . see ( tk . END )
except :
pass
def Close ( self ) :
sys . stdout = self . previous_stdout
sys . stderr = self . previous_stderr
def flush ( self ) :
sys . stdout = self . previous_stdout
sys . stderr = self . previous_stderr
def __del__ ( self ) :
sys . stdout = self . previous_stdout
2018-07-21 00:07:04 +00:00
sys . stderr = self . previous_stderr
2018-07-11 19:19:24 +00:00
2018-07-27 19:45:38 +00:00
# ---------------------------------------------------------------------- #
# Output #
# Routes stdout, stderr to a scrolled window #
# ---------------------------------------------------------------------- #
2018-07-11 19:19:24 +00:00
class Output ( Element ) :
2018-08-26 19:20:54 +00:00
def __init__ ( self , scale = ( None , None ) , size = ( None , None ) , background_color = None , text_color = None , pad = None , font = None ) :
2018-07-28 18:41:56 +00:00
'''
Output Element - reroutes stdout , stderr to this window
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param background_color : Color for Element . Text or RGB Hex
'''
2018-07-11 19:19:24 +00:00
self . TKOut = None
2018-07-26 23:53:17 +00:00
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
2018-08-16 13:21:30 +00:00
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
2018-08-26 19:20:54 +00:00
super ( ) . __init__ ( ELEM_TYPE_OUTPUT , scale = scale , size = size , background_color = bg , text_color = fg , pad = pad , font = font )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
try :
self . TKOut . __del__ ( )
except :
pass
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# Button Class #
# ---------------------------------------------------------------------- #
class Button ( Element ) :
2018-08-28 20:42:51 +00:00
def __init__ ( self , button_type = BUTTON_TYPE_CLOSES_WIN , target = ( None , None ) , button_text = ' ' , file_types = ( ( " ALL Files " , " *.* " ) , ) , image_filename = None , image_size = ( None , None ) , image_subsample = None , border_width = None , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Button Element - Specifies all types of buttons
: param button_type :
: param target :
: param button_text :
: param file_types :
: param image_filename :
: param image_size :
: param image_subsample :
: param border_width :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_button :
: param button_color :
: param font :
'''
2018-07-27 19:45:38 +00:00
self . AutoSizeButton = auto_size_button
2018-07-16 18:52:16 +00:00
self . BType = button_type
self . FileTypes = file_types
2018-07-11 19:19:24 +00:00
self . TKButton = None
2018-07-16 18:52:16 +00:00
self . Target = target
self . ButtonText = button_text
self . ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR
2018-07-24 18:12:39 +00:00
self . ImageFilename = image_filename
self . ImageSize = image_size
self . ImageSubsample = image_subsample
2018-07-11 19:19:24 +00:00
self . UserData = None
2018-07-24 18:12:39 +00:00
self . BorderWidth = border_width if border_width is not None else DEFAULT_BORDER_WIDTH
2018-08-09 13:35:21 +00:00
self . BindReturnKey = bind_return_key
self . Focus = focus
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_BUTTON , scale = scale , size = size , font = font , pad = pad )
2018-07-11 19:19:24 +00:00
return
2018-07-31 03:50:52 +00:00
def ButtonReleaseCallBack ( self , parm ) :
r , c = self . Position
2018-08-12 21:45:12 +00:00
self . ParentForm . LastButtonClicked = None
2018-07-31 03:50:52 +00:00
def ButtonPressCallBack ( self , parm ) :
r , c = self . Position
2018-08-12 21:45:12 +00:00
self . ParentForm . LastButtonClicked = self . ButtonText
2018-07-31 03:50:52 +00:00
2018-07-11 19:19:24 +00:00
# ------- Button Callback ------- #
def ButtonCallBack ( self ) :
global _my_windows
# Buttons modify targets or return from the form
# If modifying target, get the element object at the target and modify its StrVar
target = self . Target
if target [ 0 ] == ThisRow :
target = [ self . Position [ 0 ] , target [ 1 ] ]
if target [ 1 ] < 0 :
target [ 1 ] = self . Position [ 1 ] + target [ 1 ]
strvar = None
if target [ 0 ] != None :
2018-07-14 01:43:01 +00:00
if target [ 0 ] < 0 :
target = [ self . Position [ 0 ] + target [ 0 ] , target [ 1 ] ]
2018-07-11 19:19:24 +00:00
target_element = self . ParentForm . GetElementAtLocation ( target )
try :
strvar = target_element . TKStringVar
except : pass
else :
strvar = None
filetypes = [ ] if self . FileTypes is None else self . FileTypes
2018-07-31 03:50:52 +00:00
if self . BType == BUTTON_TYPE_BROWSE_FOLDER :
2018-07-11 19:19:24 +00:00
folder_name = tk . filedialog . askdirectory ( ) # show the 'get folder' dialog box
try :
strvar . set ( folder_name )
except : pass
2018-07-31 03:50:52 +00:00
elif self . BType == BUTTON_TYPE_BROWSE_FILE :
2018-07-11 19:19:24 +00:00
file_name = tk . filedialog . askopenfilename ( filetypes = filetypes ) # show the 'get file' dialog box
strvar . set ( file_name )
2018-08-27 13:56:35 +00:00
elif self . BType == BUTTON_TYPE_BROWSE_FILES :
file_name = tk . filedialog . askopenfilenames ( filetypes = filetypes )
file_name = ' ; ' . join ( file_name )
strvar . set ( file_name )
2018-08-18 20:55:21 +00:00
elif self . BType == BUTTON_TYPE_SAVEAS_FILE :
file_name = tk . filedialog . asksaveasfilename ( filetypes = filetypes ) # show the 'get file' dialog box
strvar . set ( file_name )
2018-07-31 03:50:52 +00:00
elif self . BType == BUTTON_TYPE_CLOSES_WIN : # this is a return type button so GET RESULTS and destroy window
2018-07-11 19:19:24 +00:00
# first, get the results table built
# modify the Results table in the parent FlexForm object
r , c = self . Position
2018-08-12 21:45:12 +00:00
self . ParentForm . LastButtonClicked = self . ButtonText
2018-08-14 20:49:36 +00:00
self . ParentForm . FormRemainedOpen = False
2018-07-11 19:19:24 +00:00
# if the form is tabbed, must collect all form's results and destroy all forms
if self . ParentForm . IsTabbedForm :
2018-08-06 16:20:18 +00:00
self . ParentForm . UberParent . _Close ( )
2018-07-11 19:19:24 +00:00
else :
2018-08-06 16:20:18 +00:00
self . ParentForm . _Close ( )
2018-07-11 19:19:24 +00:00
self . ParentForm . TKroot . quit ( )
if self . ParentForm . NonBlocking :
self . ParentForm . TKroot . destroy ( )
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-07-31 03:50:52 +00:00
elif self . BType == BUTTON_TYPE_READ_FORM : # LEAVE THE WINDOW OPEN!! DO NOT CLOSE
2018-07-11 19:19:24 +00:00
# first, get the results table built
# modify the Results table in the parent FlexForm object
2018-08-12 21:45:12 +00:00
self . ParentForm . LastButtonClicked = self . ButtonText
2018-08-14 20:49:36 +00:00
self . ParentForm . FormRemainedOpen = True
2018-07-11 19:19:24 +00:00
self . ParentForm . TKroot . quit ( ) # kick the users out of the mainloop
return
2018-08-23 18:17:46 +00:00
def Update ( self , new_text , button_color = ( None , None ) ) :
2018-08-23 18:10:58 +00:00
try :
self . TKButton . configure ( text = new_text )
2018-08-23 18:17:46 +00:00
if button_color != ( None , None ) :
self . TKButton . config ( foreground = button_color [ 0 ] , background = button_color [ 1 ] )
2018-08-23 18:10:58 +00:00
except :
return
2018-08-23 17:50:20 +00:00
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
try :
self . TKButton . __del__ ( )
except :
pass
super ( ) . __del__ ( )
# ---------------------------------------------------------------------- #
# ProgreessBar #
# ---------------------------------------------------------------------- #
class ProgressBar ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , max_value , orientation = None , scale = ( None , None ) , size = ( None , None ) , auto_size_text = None , bar_color = ( None , None ) , style = None , border_width = None , relief = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Progress Bar Element
: param max_value :
: param orientation :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param auto_size_text : True if should shrink field to fit the default text
: param bar_color :
: param style :
: param border_width :
: param relief :
'''
2018-07-16 18:52:16 +00:00
self . MaxValue = max_value
2018-07-11 19:19:24 +00:00
self . TKProgressBar = None
self . Cancelled = False
self . NotRunning = True
2018-07-16 18:52:16 +00:00
self . Orientation = orientation if orientation else DEFAULT_METER_ORIENTATION
self . BarColor = bar_color
self . BarStyle = style if style else DEFAULT_PROGRESS_BAR_STYLE
2018-07-18 19:11:22 +00:00
self . BorderWidth = border_width if border_width else DEFAULT_PROGRESS_BAR_BORDER_WIDTH
2018-07-16 18:52:16 +00:00
self . Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF
2018-07-11 19:19:24 +00:00
self . BarExpired = False
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_PROGRESS_BAR , scale = scale , size = size , auto_size_text = auto_size_text , pad = pad )
2018-07-11 19:19:24 +00:00
return
2018-08-25 02:47:21 +00:00
def UpdateBar ( self , current_count , max = None ) :
2018-07-11 19:19:24 +00:00
if self . ParentForm . TKrootDestroyed :
return False
2018-08-25 02:47:21 +00:00
self . TKProgressBar . Update ( current_count , max = max )
2018-07-11 19:19:24 +00:00
try :
self . ParentForm . TKroot . update ( )
except :
2018-08-25 03:32:12 +00:00
_my_windows . Decrement ( )
2018-07-11 19:19:24 +00:00
return False
return True
def __del__ ( self ) :
try :
self . TKProgressBar . __del__ ( )
except :
pass
super ( ) . __del__ ( )
2018-07-21 00:07:04 +00:00
# ---------------------------------------------------------------------- #
# Image #
# ---------------------------------------------------------------------- #
class Image ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , filename = None , data = None , scale = ( None , None ) , size = ( None , None ) , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Image Element
: param filename :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
'''
2018-07-21 00:07:04 +00:00
self . Filename = filename
2018-08-19 03:19:11 +00:00
self . Data = data
self . tktext_label = None
if data is None and filename is None :
print ( ' * Warning... no image specified in Image Element! * ' )
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_IMAGE , scale = scale , size = size , pad = pad )
2018-07-21 00:07:04 +00:00
return
2018-08-19 03:19:11 +00:00
def Update ( self , filename = None , data = None ) :
if filename is not None :
image = tk . PhotoImage ( file = filename )
elif data is not None :
2018-08-21 17:10:05 +00:00
if type ( data ) is bytes :
image = tk . PhotoImage ( data = data )
else :
image = data
2018-08-19 03:19:11 +00:00
else : return
2018-08-22 17:58:04 +00:00
width , height = image . width ( ) , image . height ( )
self . tktext_label . configure ( image = image , width = width , height = height )
2018-08-19 03:19:11 +00:00
self . tktext_label . image = image
2018-07-21 00:07:04 +00:00
def __del__ ( self ) :
super ( ) . __del__ ( )
2018-08-26 19:20:54 +00:00
# ---------------------------------------------------------------------- #
# Canvas #
# ---------------------------------------------------------------------- #
class Canvas ( Element ) :
2018-08-27 02:16:54 +00:00
def __init__ ( self , canvas = None , background_color = None , scale = ( None , None ) , size = ( None , None ) , pad = None ) :
2018-08-26 19:20:54 +00:00
self . BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
2018-08-27 02:16:54 +00:00
self . TKCanvas = canvas
2018-08-26 19:20:54 +00:00
super ( ) . __init__ ( ELEM_TYPE_CANVAS , background_color = background_color , scale = scale , size = size , pad = pad )
return
def __del__ ( self ) :
super ( ) . __del__ ( )
2018-08-27 02:16:54 +00:00
# ---------------------------------------------------------------------- #
# Frame #
# ---------------------------------------------------------------------- #
class Frame ( Element ) :
def __init__ ( self , frame = None , background_color = None , scale = ( None , None ) , size = ( None , None ) , pad = None ) :
self . BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self . TKFrame = frame
super ( ) . __init__ ( ELEM_TYPE_FRAME , background_color = background_color , scale = scale , size = size , pad = pad )
return
def __del__ ( self ) :
super ( ) . __del__ ( )
2018-07-23 19:14:34 +00:00
# ---------------------------------------------------------------------- #
# Slider #
# ---------------------------------------------------------------------- #
class Slider ( Element ) :
2018-08-27 15:20:01 +00:00
def __init__ ( self , range = ( None , None ) , default_value = None , resolution = None , orientation = None , border_width = None , relief = None , scale = ( None , None ) , size = ( None , None ) , font = None , background_color = None , text_color = None , key = None , pad = None ) :
2018-07-28 18:41:56 +00:00
'''
Slider Element
: param range :
: param default_value :
: param orientation :
: param border_width :
: param relief :
: param scale : Adds multiplier to size ( w , h )
: param size : Size of field in characters
: param background_color : Color for Element . Text or RGB Hex
: param font :
'''
2018-07-23 19:14:34 +00:00
self . TKScale = None
self . Range = ( 1 , 10 ) if range == ( None , None ) else range
self . DefaultValue = 5 if default_value is None else default_value
self . Orientation = orientation if orientation else DEFAULT_SLIDER_ORIENTATION
self . BorderWidth = border_width if border_width else DEFAULT_SLIDER_BORDER_WIDTH
self . Relief = relief if relief else DEFAULT_SLIDER_RELIEF
2018-08-27 15:20:01 +00:00
self . Resolution = 1 if resolution is None else resolution
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_INPUT_SLIDER , scale = scale , size = size , font = font , background_color = background_color , text_color = text_color , key = key , pad = pad )
2018-07-23 19:14:34 +00:00
return
2018-08-25 02:47:21 +00:00
def Update ( self , value , range = ( None , None ) ) :
2018-08-24 11:45:31 +00:00
self . TKIntVar . set ( value )
2018-08-25 02:47:21 +00:00
if range != ( None , None ) :
self . TKScale . config ( from_ = range [ 0 ] , to_ = range [ 1 ] )
2018-08-24 11:45:31 +00:00
2018-07-23 19:14:34 +00:00
def __del__ ( self ) :
super ( ) . __del__ ( )
2018-07-21 00:07:04 +00:00
2018-08-14 20:49:36 +00:00
# ---------------------------------------------------------------------- #
# Column #
# ---------------------------------------------------------------------- #
class Column ( Element ) :
2018-08-26 02:57:15 +00:00
def __init__ ( self , layout , background_color = None , pad = None ) :
2018-08-14 20:49:36 +00:00
self . UseDictionary = False
self . ReturnValues = None
self . ReturnValuesList = [ ]
self . ReturnValuesDictionary = { }
self . DictionaryKeyCounter = 0
self . ParentWindow = None
self . Rows = [ ]
self . ParentForm = None
self . TKFrame = None
2018-08-18 20:52:24 +00:00
bg = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
2018-08-14 20:49:36 +00:00
self . Layout ( layout )
2018-08-26 02:57:15 +00:00
super ( ) . __init__ ( ELEM_TYPE_COLUMN , background_color = background_color , pad = pad )
2018-08-14 20:49:36 +00:00
return
def AddRow ( self , * args ) :
''' Parms are a variable number of Elements '''
NumRows = len ( self . Rows ) # number of existing rows is our row number
CurrentRowNumber = NumRows # this row's number
CurrentRow = [ ] # start with a blank row and build up
# ------------------------- Add the elements to a row ------------------------- #
for i , element in enumerate ( args ) : # Loop through list of elements and add them to the row
element . Position = ( CurrentRowNumber , i )
CurrentRow . append ( element )
if element . Key is not None :
self . UseDictionary = True
# ------------------------- Append the row to list of Rows ------------------------- #
self . Rows . append ( CurrentRow )
def Layout ( self , rows ) :
for row in rows :
self . AddRow ( * row )
def __del__ ( self ) :
for row in self . Rows :
for element in row :
element . __del__ ( )
try :
2018-08-19 03:19:11 +00:00
del ( self . TKFrame )
2018-08-14 20:49:36 +00:00
except :
pass
super ( ) . __del__ ( )
2018-07-21 00:07:04 +00:00
2018-07-11 19:19:24 +00:00
# ------------------------------------------------------------------------- #
# FlexForm CLASS #
# ------------------------------------------------------------------------- #
class FlexForm :
'''
Display a user defined for and return the filled in data
'''
2018-08-28 16:25:15 +00:00
def __init__ ( self , title , default_element_size = DEFAULT_ELEMENT_SIZE , default_button_element_size = ( None , None ) , auto_size_text = None , auto_size_buttons = None , scale = ( None , None ) , location = ( None , None ) , button_color = None , font = None , progress_bar_color = ( None , None ) , background_color = None , is_tabbed_form = False , border_depth = None , auto_close = False , auto_close_duration = DEFAULT_AUTOCLOSE_TIME , icon = DEFAULT_WINDOW_ICON , return_keyboard_events = False , use_default_focus = True , text_justification = None ) :
2018-07-27 19:45:38 +00:00
self . AutoSizeText = auto_size_text if auto_size_text is not None else DEFAULT_AUTOSIZE_TEXT
self . AutoSizeButtons = auto_size_buttons if auto_size_buttons is not None else DEFAULT_AUTOSIZE_BUTTONS
2018-07-11 19:19:24 +00:00
self . Title = title
self . Rows = [ ] # a list of ELEMENTS for this row
2018-07-16 18:52:16 +00:00
self . DefaultElementSize = default_element_size
2018-08-28 16:25:15 +00:00
self . DefaultButtonElementSize = default_button_element_size if default_button_element_size != ( None , None ) else DEFAULT_BUTTON_ELEMENT_SIZE
2018-07-16 18:52:16 +00:00
self . Scale = scale
self . Location = location
self . ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR
2018-07-26 23:53:17 +00:00
self . BackgroundColor = background_color if background_color else DEFAULT_BACKGROUND_COLOR
2018-07-16 18:52:16 +00:00
self . IsTabbedForm = is_tabbed_form
2018-07-11 19:19:24 +00:00
self . ParentWindow = None
2018-07-16 18:52:16 +00:00
self . Font = font if font else DEFAULT_FONT
2018-07-11 19:19:24 +00:00
self . RadioDict = { }
2018-07-16 18:52:16 +00:00
self . BorderDepth = border_depth
self . WindowIcon = icon if not _my_windows . user_defined_icon else _my_windows . user_defined_icon
self . AutoClose = auto_close
2018-07-11 19:19:24 +00:00
self . NonBlocking = False
self . TKroot = None
self . TKrootDestroyed = False
2018-08-14 20:49:36 +00:00
self . FormRemainedOpen = False
2018-07-11 19:19:24 +00:00
self . TKAfterID = None
2018-07-16 18:52:16 +00:00
self . ProgressBarColor = progress_bar_color
self . AutoCloseDuration = auto_close_duration
2018-07-11 19:19:24 +00:00
self . UberParent = None
self . RootNeedsDestroying = False
self . Shown = False
self . ReturnValues = None
2018-08-12 21:45:12 +00:00
self . ReturnValuesList = [ ]
self . ReturnValuesDictionary = { }
self . DictionaryKeyCounter = 0
self . LastButtonClicked = None
2018-08-09 15:37:44 +00:00
self . UseDictionary = False
2018-08-21 00:47:24 +00:00
self . UseDefaultFocus = use_default_focus
2018-08-20 17:48:02 +00:00
self . ReturnKeyboardEvents = return_keyboard_events
self . LastKeyboardEvent = None
2018-08-21 22:29:32 +00:00
self . TextJustification = text_justification
2018-07-11 19:19:24 +00:00
# ------------------------- Add ONE Row to Form ------------------------- #
2018-08-07 11:30:51 +00:00
def AddRow ( self , * args ) :
2018-07-11 19:19:24 +00:00
''' Parms are a variable number of Elements '''
NumRows = len ( self . Rows ) # number of existing rows is our row number
CurrentRowNumber = NumRows # this row's number
2018-08-07 11:30:51 +00:00
CurrentRow = [ ] # start with a blank row and build up
2018-07-11 19:19:24 +00:00
# ------------------------- Add the elements to a row ------------------------- #
for i , element in enumerate ( args ) : # Loop through list of elements and add them to the row
element . Position = ( CurrentRowNumber , i )
2018-08-07 11:30:51 +00:00
CurrentRow . append ( element )
2018-07-11 19:19:24 +00:00
# ------------------------- Append the row to list of Rows ------------------------- #
self . Rows . append ( CurrentRow )
# ------------------------- Add Multiple Rows to Form ------------------------- #
def AddRows ( self , rows ) :
for row in rows :
self . AddRow ( * row )
2018-07-23 19:14:34 +00:00
def Layout ( self , rows ) :
self . AddRows ( rows )
def LayoutAndShow ( self , rows , non_blocking = False ) :
2018-07-11 19:19:24 +00:00
self . AddRows ( rows )
2018-07-23 19:14:34 +00:00
self . Show ( non_blocking = non_blocking )
return self . ReturnValues
def LayoutAndRead ( self , rows , non_blocking = False ) :
self . AddRows ( rows )
self . Show ( non_blocking = non_blocking )
2018-07-11 19:19:24 +00:00
return self . ReturnValues
# ------------------------- ShowForm THIS IS IT! ------------------------- #
2018-07-16 18:52:16 +00:00
def Show ( self , non_blocking = False ) :
2018-07-11 19:19:24 +00:00
self . Shown = True
# Compute num rows & num cols (it'll come in handy debugging)
self . NumRows = len ( self . Rows )
2018-08-07 11:30:51 +00:00
self . NumCols = max ( len ( row ) for row in self . Rows )
2018-07-16 18:52:16 +00:00
self . NonBlocking = non_blocking
2018-07-11 19:19:24 +00:00
2018-08-09 13:35:21 +00:00
# Search through entire form to see if any elements set the focus
# if not, then will set the focus to the first input element
found_focus = False
for row in self . Rows :
for element in row :
try :
if element . Focus :
found_focus = True
except :
pass
2018-08-09 15:37:44 +00:00
try :
if element . Key is not None :
self . UseDictionary = True
except :
pass
2018-08-21 00:47:24 +00:00
if not found_focus and self . UseDefaultFocus :
2018-08-09 13:35:21 +00:00
self . UseDefaultFocus = True
2018-08-21 00:47:24 +00:00
else :
self . UseDefaultFocus = False
2018-07-11 19:19:24 +00:00
# -=-=-=-=-=-=-=-=- RUN the GUI -=-=-=-=-=-=-=-=- ##
StartupTK ( self )
2018-08-21 14:33:42 +00:00
# If a button or keyboard event happened but no results have been built, build the results
if self . LastKeyboardEvent is not None or self . LastButtonClicked is not None :
return BuildResults ( self , False , self )
2018-07-11 19:19:24 +00:00
return self . ReturnValues
# ------------------------- SetIcon - set the window's fav icon ------------------------- #
2018-07-16 18:52:16 +00:00
def SetIcon ( self , icon ) :
self . WindowIcon = icon
2018-07-11 19:19:24 +00:00
try :
2018-07-16 18:52:16 +00:00
self . TKroot . iconbitmap ( icon )
2018-07-11 19:19:24 +00:00
except : pass
2018-07-16 18:52:16 +00:00
def GetElementAtLocation ( self , location ) :
( row_num , col_num ) = location
2018-07-11 19:19:24 +00:00
row = self . Rows [ row_num ]
2018-08-07 11:30:51 +00:00
element = row [ col_num ]
2018-07-11 19:19:24 +00:00
return element
2018-08-06 16:20:18 +00:00
def _GetDefaultElementSize ( self ) :
2018-07-11 19:19:24 +00:00
return self . DefaultElementSize
2018-08-06 16:20:18 +00:00
def _AutoCloseAlarmCallback ( self ) :
2018-07-11 19:19:24 +00:00
try :
if self . UberParent :
window = self . UberParent
else :
window = self
if window :
2018-08-06 16:20:18 +00:00
window . _Close ( )
2018-07-11 19:19:24 +00:00
self . TKroot . quit ( )
self . RootNeedsDestroying = True
except :
pass
def Read ( self ) :
2018-08-27 02:16:54 +00:00
self . NonBlocking = False
2018-07-23 19:14:34 +00:00
if self . TKrootDestroyed :
return None , None
if not self . Shown :
2018-07-11 19:19:24 +00:00
self . Show ( )
2018-07-23 19:14:34 +00:00
else :
2018-08-20 17:48:02 +00:00
InitializeResults ( self )
2018-07-11 19:19:24 +00:00
self . TKroot . mainloop ( )
if self . RootNeedsDestroying :
self . TKroot . destroy ( )
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-08-27 14:55:53 +00:00
# if form was closed with X
if self . LastButtonClicked is None and self . LastKeyboardEvent is None and self . ReturnValues [ 0 ] is None :
_my_windows . Decrement ( )
2018-08-20 17:48:02 +00:00
if self . LastKeyboardEvent is not None or self . LastButtonClicked is not None :
return BuildResults ( self , False , self )
else :
return self . ReturnValues
2018-07-23 19:14:34 +00:00
def ReadNonBlocking ( self , Message = ' ' ) :
if self . TKrootDestroyed :
return None , None
2018-08-10 13:15:19 +00:00
if not self . Shown :
self . Show ( non_blocking = True )
2018-07-23 19:14:34 +00:00
if Message :
print ( Message )
try :
rc = self . TKroot . update ( )
except :
self . TKrootDestroyed = True
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-08-23 18:10:58 +00:00
# return None, None
2018-08-14 20:49:36 +00:00
return BuildResults ( self , False , self )
2018-07-11 19:19:24 +00:00
2018-08-28 23:02:02 +00:00
def Refresh ( self ) :
if self . TKrootDestroyed :
return
try :
rc = self . TKroot . update ( )
except :
pass
2018-08-22 21:47:32 +00:00
def GetScreenDimensions ( self ) :
if self . TKrootDestroyed :
return None , None
screen_width = self . TKroot . winfo_screenwidth ( ) # get window info to move to middle of screen
screen_height = self . TKroot . winfo_screenheight ( )
return screen_width , screen_height
2018-08-20 17:48:02 +00:00
def KeyboardCallback ( self , event ) :
self . LastButtonClicked = None
self . FormRemainedOpen = True
if event . char != ' ' :
self . LastKeyboardEvent = event . char
else :
self . LastKeyboardEvent = str ( event . keysym ) + ' : ' + str ( event . keycode )
if not self . NonBlocking :
2018-08-21 22:29:32 +00:00
BuildResults ( self , False , self )
2018-08-20 17:48:02 +00:00
self . TKroot . quit ( )
2018-08-21 03:45:09 +00:00
def MouseWheelCallback ( self , event ) :
self . LastButtonClicked = None
self . FormRemainedOpen = True
2018-08-22 00:21:03 +00:00
self . LastKeyboardEvent = ' MouseWheel:Down ' if event . delta < 0 else ' MouseWheel:Up '
2018-08-21 03:45:09 +00:00
if not self . NonBlocking :
2018-08-21 22:29:32 +00:00
BuildResults ( self , False , self )
2018-08-21 03:45:09 +00:00
self . TKroot . quit ( )
2018-07-11 19:19:24 +00:00
2018-08-06 16:20:18 +00:00
def _Close ( self ) :
2018-07-11 19:19:24 +00:00
try :
self . TKroot . update ( )
except : pass
2018-07-21 00:07:04 +00:00
if not self . NonBlocking :
2018-08-21 22:29:32 +00:00
BuildResults ( self , False , self )
2018-07-11 19:19:24 +00:00
if self . TKrootDestroyed :
2018-07-21 00:07:04 +00:00
return None
2018-07-11 19:19:24 +00:00
self . TKrootDestroyed = True
self . RootNeedsDestroying = True
2018-07-21 00:07:04 +00:00
return None
2018-07-11 19:19:24 +00:00
2018-07-17 17:43:58 +00:00
def CloseNonBlockingForm ( self ) :
2018-08-18 20:55:21 +00:00
if self . TKrootDestroyed :
return
2018-07-21 00:07:04 +00:00
try :
self . TKroot . destroy ( )
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-07-21 00:07:04 +00:00
except : pass
2018-07-17 17:43:58 +00:00
2018-07-11 19:19:24 +00:00
def OnClosingCallback ( self ) :
return
def __enter__ ( self ) :
return self
def __exit__ ( self , * a ) :
self . __del__ ( )
2018-07-17 17:43:58 +00:00
return False
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
for row in self . Rows :
2018-08-07 11:30:51 +00:00
for element in row :
2018-07-11 19:19:24 +00:00
element . __del__ ( )
2018-08-14 20:49:36 +00:00
# try:
# del(self.TKroot)
# except:
# pass
2018-07-11 19:19:24 +00:00
# ------------------------------------------------------------------------- #
# UberForm CLASS #
# Used to make forms into TABS (it's trick) #
# ------------------------------------------------------------------------- #
class UberForm ( ) :
FormList = None # list of all the forms in this window
FormReturnValues = None
TKroot = None # tk root for the overall window
TKrootDestroyed = False
def __init__ ( self ) :
self . FormList = [ ]
self . FormReturnValues = [ ]
self . TKroot = None
self . TKrootDestroyed = False
2018-07-16 18:52:16 +00:00
def AddForm ( self , form ) :
self . FormList . append ( form )
2018-07-11 19:19:24 +00:00
2018-08-06 16:20:18 +00:00
def _Close ( self ) :
2018-07-11 19:19:24 +00:00
self . FormReturnValues = [ ]
for form in self . FormList :
2018-08-06 16:20:18 +00:00
form . _Close ( )
2018-07-11 19:19:24 +00:00
self . FormReturnValues . append ( form . ReturnValues )
if not self . TKrootDestroyed :
self . TKrootDestroyed = True
self . TKroot . destroy ( )
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-07-11 19:19:24 +00:00
def __del__ ( self ) :
return
# ====================================================================== #
# BUTTON Lazy Functions #
# ====================================================================== #
# ------------------------- INPUT TEXT Element lazy functions ------------------------- #
2018-08-26 19:20:54 +00:00
In = InputText
Input = InputText
#### TODO REMOVE THESE COMMENTS - was the old way, but want to keep around for a bit just in case
# def In(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None, password_char='', background_color=None, text_color=None, do_not_clear=False, key=None, focus=False):
# return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text, password_char=password_char, background_color=background_color, text_color=text_color, do_not_clear=do_not_clear, focus=focus, key=key)
2018-07-11 19:19:24 +00:00
2018-08-26 19:20:54 +00:00
# def Input(default_text ='', scale=(None, None), size=(None, None), auto_size_text=None, password_char='', background_color=None, text_color=None, do_not_clear=False, key=None, focus=False):
# return InputText(default_text=default_text, scale=scale, size=size, auto_size_text=auto_size_text, password_char=password_char, background_color=background_color, text_color=text_color, do_not_clear=do_not_clear, focus=focus, key=key)
2018-07-11 19:19:24 +00:00
2018-08-22 15:37:51 +00:00
# ------------------------- CHECKBOX Element lazy functions ------------------------- #
CB = Checkbox
CBox = Checkbox
Check = Checkbox
2018-07-21 00:07:04 +00:00
# ------------------------- INPUT COMBO Element lazy functions ------------------------- #
2018-08-26 19:20:54 +00:00
Combo = InputCombo
DropDown = InputCombo
Drop = InputCombo
# def Combo(values, scale=(None, None), size=(None, None), auto_size_text=None, background_color=None):
# return InputCombo(values=values, scale=scale, size=size, auto_size_text=auto_size_text, background_color=background_color)
#
# def DropDown(values, scale=(None, None), size=(None, None), auto_size_text=None):
# return InputCombo(values=values, scale=scale, size=size, auto_size_text=auto_size_text)
#
# def Drop(values, scale=(None, None), size=(None, None), auto_size_text=None):
# return InputCombo(values=values, scale=scale, size=size, auto_size_text=auto_size_text)
2018-07-11 19:19:24 +00:00
# ------------------------- TEXT Element lazy functions ------------------------- #
2018-08-26 19:20:54 +00:00
Txt = Text
T = Text
# def Txt(display_text, scale=(None, None), size=(None, None), auto_size_text=None, font=None, text_color=None, justification=None):
# return Text(display_text, scale=scale, size=size, auto_size_text=auto_size_text, font=font, text_color=text_color, justification=justification)
#
# def T(display_text, scale=(None, None), size=(None, None), auto_size_text=None, font=None, text_color=None, justification=None):
# return Text(display_text, scale=scale, size=size, auto_size_text=auto_size_text, font=font, text_color=text_color, justification=justification)
2018-07-11 19:19:24 +00:00
# ------------------------- FOLDER BROWSE Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def FolderBrowse ( target = ( ThisRow , - 1 ) , button_text = ' Browse ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , pad = None ) :
return Button ( BUTTON_TYPE_BROWSE_FOLDER , target = target , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- FILE BROWSE Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def FileBrowse ( target = ( ThisRow , - 1 ) , file_types = ( ( " ALL Files " , " *.* " ) , ) , button_text = ' Browse ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , pad = None ) :
return Button ( BUTTON_TYPE_BROWSE_FILE , target , button_text = button_text , file_types = file_types , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , pad = pad )
2018-08-18 20:55:21 +00:00
2018-08-27 13:56:35 +00:00
# ------------------------- FILES BROWSE Element (Multiple file selection) lazy function ------------------------- #
def FilesBrowse ( target = ( ThisRow , - 1 ) , file_types = ( ( " ALL Files " , " *.* " ) , ) , button_text = ' Browse ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , pad = None ) :
return Button ( BUTTON_TYPE_BROWSE_FILES , target , button_text = button_text , file_types = file_types , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , pad = pad )
2018-08-18 20:55:21 +00:00
# ------------------------- FILE BROWSE Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def FileSaveAs ( target = ( ThisRow , - 1 ) , file_types = ( ( " ALL Files " , " *.* " ) , ) , button_text = ' Save As... ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , pad = None ) :
return Button ( BUTTON_TYPE_SAVEAS_FILE , target , button_text = button_text , file_types = file_types , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , pad = pad )
2018-08-18 20:55:21 +00:00
# ------------------------- SAVE AS Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def SaveAs ( target = ( ThisRow , - 1 ) , file_types = ( ( " ALL Files " , " *.* " ) , ) , button_text = ' Save As... ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , pad = None ) :
return Button ( BUTTON_TYPE_SAVEAS_FILE , target , button_text = button_text , file_types = file_types , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , pad = pad )
2018-08-18 20:55:21 +00:00
# ------------------------- SAVE BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Save ( button_text = ' Save ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , bind_return_key = True , font = None , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- SUBMIT BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Submit ( button_text = ' Submit ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , bind_return_key = True , font = None , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
2018-08-28 23:02:02 +00:00
# ------------------------- OPEN BUTTON Element lazy function ------------------------- #
def Open ( button_text = ' Open ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , bind_return_key = True , font = None , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- OK BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def OK ( button_text = ' OK ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , bind_return_key = True , font = None , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- YES BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Ok ( button_text = ' Ok ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , bind_return_key = True , font = None , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- CANCEL BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Cancel ( button_text = ' Cancel ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
2018-07-16 22:36:17 +00:00
# ------------------------- QUIT BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Quit ( button_text = ' Quit ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-16 22:36:17 +00:00
2018-08-18 20:55:21 +00:00
# ------------------------- Exit BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Exit ( button_text = ' Exit ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-08-18 20:55:21 +00:00
2018-07-11 19:19:24 +00:00
# ------------------------- YES BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def Yes ( button_text = ' Yes ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = True , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- NO BUTTON Element lazy function ------------------------- #
2018-08-26 02:57:15 +00:00
def No ( button_text = ' No ' , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- GENERIC BUTTON Element lazy function ------------------------- #
# this is the only button that REQUIRES button text field
2018-08-26 02:57:15 +00:00
def SimpleButton ( button_text , image_filename = None , image_size = ( None , None ) , image_subsample = None , border_width = None , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_CLOSES_WIN , image_filename = image_filename , image_size = image_size , image_subsample = image_subsample , button_text = button_text , border_width = border_width , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
# ------------------------- GENERIC BUTTON Element lazy function ------------------------- #
# this is the only button that REQUIRES button text field
2018-08-28 23:02:02 +00:00
def ReadFormButton ( button_text , image_filename = None , image_size = ( None , None ) , image_subsample = None , border_width = None , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
2018-08-28 20:42:51 +00:00
return Button ( BUTTON_TYPE_READ_FORM , image_filename = image_filename , image_size = image_size , image_subsample = image_subsample , border_width = border_width , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-31 03:50:52 +00:00
2018-08-26 02:57:15 +00:00
def RealtimeButton ( button_text , image_filename = None , image_size = ( None , None ) , image_subsample = None , border_width = None , scale = ( None , None ) , size = ( None , None ) , auto_size_button = None , button_color = None , font = None , bind_return_key = False , focus = False , pad = None ) :
return Button ( BUTTON_TYPE_REALTIME , image_filename = image_filename , image_size = image_size , image_subsample = image_subsample , border_width = border_width , button_text = button_text , scale = scale , size = size , auto_size_button = auto_size_button , button_color = button_color , font = font , bind_return_key = bind_return_key , focus = focus , pad = pad )
2018-07-11 19:19:24 +00:00
2018-08-12 21:45:12 +00:00
##################################### ----- RESULTS ------ ##################################################
def AddToReturnDictionary ( form , element , value ) :
if element . Key is None :
form . ReturnValuesDictionary [ form . DictionaryKeyCounter ] = value
form . DictionaryKeyCounter + = 1
else :
form . ReturnValuesDictionary [ element . Key ] = value
def AddToReturnList ( form , value ) :
form . ReturnValuesList . append ( value )
#----------------------------------------------------------------------------#
# ------- FUNCTION InitializeResults. Sets up form results matrix --------#
2018-07-11 19:19:24 +00:00
def InitializeResults ( form ) :
2018-08-14 20:49:36 +00:00
BuildResults ( form , True , form )
2018-07-11 19:19:24 +00:00
return
#===== Radio Button RadVar encoding and decoding =====#
#===== The value is simply the row * 1000 + col =====#
def DecodeRadioRowCol ( RadValue ) :
row = RadValue / / 1000
col = RadValue % 1000
return row , col
2018-07-16 18:52:16 +00:00
def EncodeRadioRowCol ( row , col ) :
RadValue = row * 1000 + col
2018-07-11 19:19:24 +00:00
return RadValue
# ------- FUNCTION BuildResults. Form exiting so build the results to pass back ------- #
# format of return values is
# (Button Pressed, input_values)
2018-08-14 20:49:36 +00:00
def BuildResults ( form , initialize_only , top_level_form ) :
2018-07-11 19:19:24 +00:00
# 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
2018-08-12 21:45:12 +00:00
form . DictionaryKeyCounter = 0
form . ReturnValuesDictionary = { }
form . ReturnValuesList = [ ]
2018-08-14 20:49:36 +00:00
BuildResultsForSubform ( form , initialize_only , top_level_form )
return form . ReturnValues
def BuildResultsForSubform ( form , initialize_only , top_level_form ) :
2018-08-28 20:42:51 +00:00
button_pressed_text = top_level_form . LastButtonClicked
2018-07-11 19:19:24 +00:00
for row_num , row in enumerate ( form . Rows ) :
2018-08-07 11:30:51 +00:00
for col_num , element in enumerate ( row ) :
2018-08-14 20:49:36 +00:00
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 )
# for key in element.ReturnValuesDictionary:
# top_level_form.ReturnValuesDictionary[key] = element.ReturnValuesDictionary[key]
# top_level_form.DictionaryKeyCounter += element.DictionaryKeyCounter
if element . UseDictionary :
top_level_form . UseDictionary = True
if element . ReturnValues [ 0 ] is not None : # if a button was clicked
button_pressed_text = element . ReturnValues [ 0 ]
2018-08-12 21:45:12 +00:00
if not initialize_only :
if element . Type == ELEM_TYPE_INPUT_TEXT :
2018-07-11 19:19:24 +00:00
value = element . TKStringVar . get ( )
2018-08-20 17:48:02 +00:00
if not top_level_form . NonBlocking and not element . do_not_clear and not top_level_form . ReturnKeyboardEvents :
2018-08-12 21:45:12 +00:00
element . TKStringVar . set ( ' ' )
elif element . Type == ELEM_TYPE_INPUT_CHECKBOX :
2018-08-14 20:49:36 +00:00
value = element . TKIntVar . get ( )
value = ( value != 0 )
2018-08-12 21:45:12 +00:00
elif element . Type == ELEM_TYPE_INPUT_RADIO :
RadVar = element . TKIntVar . get ( )
this_rowcol = EncodeRadioRowCol ( row_num , col_num )
value = RadVar == this_rowcol
elif element . Type == ELEM_TYPE_BUTTON :
2018-08-14 20:49:36 +00:00
if top_level_form . LastButtonClicked == element . ButtonText :
button_pressed_text = top_level_form . LastButtonClicked
2018-08-12 21:45:12 +00:00
if element . BType != BUTTON_TYPE_REALTIME : # Do not clear realtime buttons
2018-08-14 20:49:36 +00:00
top_level_form . LastButtonClicked = None
2018-08-12 21:45:12 +00:00
elif element . Type == ELEM_TYPE_INPUT_COMBO :
value = element . TKStringVar . get ( )
elif element . Type == ELEM_TYPE_INPUT_LISTBOX :
2018-08-14 20:49:36 +00:00
try :
items = element . TKListbox . curselection ( )
value = [ element . Values [ int ( item ) ] for item in items ]
except :
value = ' '
2018-08-12 21:45:12 +00:00
elif element . Type == ELEM_TYPE_INPUT_SPIN :
try :
value = element . TKStringVar . get ( )
except :
value = 0
elif element . Type == ELEM_TYPE_INPUT_SLIDER :
try :
value = element . TKIntVar . get ( )
except :
value = 0
elif element . Type == ELEM_TYPE_INPUT_MULTILINE :
try :
value = element . TKText . get ( 1.0 , tk . END )
2018-08-20 17:48:02 +00:00
if not top_level_form . NonBlocking and not element . do_not_clear and not top_level_form . ReturnKeyboardEvents :
2018-08-12 21:45:12 +00:00
element . TKText . delete ( ' 1.0 ' , tk . END )
except :
value = None
else :
value = None
2018-08-14 20:49:36 +00:00
2018-08-12 21:45:12 +00:00
# if an input type element, update the results
if element . Type != ELEM_TYPE_BUTTON and element . Type != ELEM_TYPE_TEXT and element . Type != ELEM_TYPE_IMAGE and \
2018-08-14 20:49:36 +00:00
element . Type != ELEM_TYPE_OUTPUT and element . Type != ELEM_TYPE_PROGRESS_BAR and element . Type != ELEM_TYPE_COLUMN :
2018-08-12 21:45:12 +00:00
AddToReturnList ( form , value )
2018-08-14 20:49:36 +00:00
AddToReturnDictionary ( top_level_form , element , value )
2018-08-08 14:40:40 +00:00
2018-08-22 19:48:28 +00:00
# if this is a column, then will fail so need to wrap with tr
try :
if form . ReturnKeyboardEvents and form . LastKeyboardEvent is not None :
button_pressed_text = form . LastKeyboardEvent
form . LastKeyboardEvent = None
except : pass
2018-08-20 17:48:02 +00:00
2018-08-08 14:40:40 +00:00
try :
2018-08-12 21:45:12 +00:00
form . ReturnValuesDictionary . pop ( None , None ) # clean up dictionary include None was included
2018-08-08 14:40:40 +00:00
except : pass
2018-07-11 19:19:24 +00:00
2018-08-12 02:31:42 +00:00
if not form . UseDictionary :
2018-08-12 21:45:12 +00:00
form . ReturnValues = button_pressed_text , form . ReturnValuesList
2018-08-12 02:31:42 +00:00
else :
2018-08-12 21:45:12 +00:00
form . ReturnValues = button_pressed_text , form . ReturnValuesDictionary
2018-08-08 14:40:40 +00:00
return form . ReturnValues
2018-07-11 19:19:24 +00:00
# ------------------------------------------------------------------------------------------------------------------ #
# ===================================== TK CODE STARTS HERE ====================================================== #
# ------------------------------------------------------------------------------------------------------------------ #
2018-08-14 20:49:36 +00:00
def PackFormIntoFrame ( form , containing_frame , toplevel_form ) :
def CharWidthInPixels ( ) :
return tkinter . font . Font ( ) . measure ( ' A ' ) # single character width
# only set title on non-tabbed forms
border_depth = toplevel_form . BorderDepth if toplevel_form . BorderDepth is not None else DEFAULT_BORDER_WIDTH
# --------------------------------------------------------------------------- #
# **************** Use FlexForm to build the tkinter window ********** ----- #
# Building is done row by row. #
# --------------------------------------------------------------------------- #
focus_set = False
######################### 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 )
for col_num , element in enumerate ( flex_row ) :
element . ParentForm = toplevel_form # save the button's parent form object
if toplevel_form . Font and ( element . Font == DEFAULT_FONT or not element . Font ) :
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
2018-08-28 16:25:15 +00:00
element_type = element . Type
# Set foreground color
text_color = element . TextColor
2018-08-14 20:49:36 +00:00
# Determine Element size
element_size = element . Size
2018-08-28 16:25:15 +00:00
if ( element_size == ( None , None ) and element_type != ELEM_TYPE_BUTTON ) : # user did not specify a size
2018-08-14 20:49:36 +00:00
element_size = toplevel_form . DefaultElementSize
2018-08-28 16:25:15 +00:00
elif ( element_size == ( None , None ) and element_type == ELEM_TYPE_BUTTON ) :
element_size = toplevel_form . DefaultButtonElementSize
2018-08-14 20:49:36 +00:00
else : auto_size_text = False # if user has specified a size then it shouldn't autosize
# Apply scaling... Element scaling is higher priority than form level
if element . Scale != ( None , None ) :
element_size = ( int ( element_size [ 0 ] * element . Scale [ 0 ] ) , int ( element_size [ 1 ] * element . Scale [ 1 ] ) )
elif toplevel_form . Scale != ( None , None ) :
element_size = ( int ( element_size [ 0 ] * toplevel_form . Scale [ 0 ] ) , int ( element_size [ 1 ] * toplevel_form . Scale [ 1 ] ) )
# ------------------------- COLUMN element ------------------------- #
if element_type == ELEM_TYPE_COLUMN :
col_frame = tk . Frame ( tk_row_frame )
PackFormIntoFrame ( element , col_frame , toplevel_form )
2018-08-27 02:16:54 +00:00
col_frame . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-18 20:52:24 +00:00
if element . BackgroundColor != COLOR_SYSTEM_DEFAULT and element . BackgroundColor is not None :
2018-08-14 20:49:36 +00:00
col_frame . configure ( background = element . BackgroundColor , highlightbackground = element . BackgroundColor , highlightcolor = element . BackgroundColor )
# ------------------------- TEXT element ------------------------- #
elif element_type == ELEM_TYPE_TEXT :
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 ( l ) for l 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 )
2018-08-26 19:20:54 +00:00
if auto_size_text :
2018-08-14 20:49:36 +00:00
width = 0
2018-08-21 22:29:32 +00:00
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 == ' left ' else tk . CENTER if justification == ' center ' else tk . RIGHT
anchor = tk . NW if justification == ' left ' else tk . N if justification == ' center ' else tk . NE
2018-08-27 02:16:54 +00:00
tktext_label = tk . Label ( tk_row_frame , textvariable = stringvar , width = width , height = height , justify = justify , bd = border_depth , font = font )
2018-08-14 20:49:36 +00:00
# Set wrap-length for text (in PIXELS) == PAIN IN THE ASS
2018-08-24 11:45:31 +00:00
wraplen = tktext_label . winfo_reqwidth ( ) + 40 # width of widget in Pixels
if not auto_size_text :
wraplen = 0
# print("wraplen, width, height", wraplen, width, height)
2018-08-27 02:16:54 +00:00
tktext_label . configure ( anchor = anchor , wraplen = wraplen ) # set wrap to width of widget
2018-08-14 20:49:36 +00:00
if element . BackgroundColor is not None :
tktext_label . configure ( background = element . BackgroundColor )
if element . TextColor != COLOR_SYSTEM_DEFAULT and element . TextColor is not None :
tktext_label . configure ( fg = element . TextColor )
2018-08-27 02:16:54 +00:00
tktext_label . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-23 16:45:51 +00:00
element . TKText = tktext_label
2018-08-14 20:49:36 +00:00
# ------------------------- BUTTON element ------------------------- #
elif element_type == ELEM_TYPE_BUTTON :
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
2018-08-28 16:25:15 +00:00
if auto_size is False or element . Size [ 0 ] is not None :
width , height = element_size
else :
width = 0
height = toplevel_form . DefaultButtonElementSize [ 1 ]
2018-08-14 20:49:36 +00:00
lines = btext . split ( ' \n ' )
max_line_len = max ( [ len ( l ) for l in lines ] )
num_lines = len ( lines )
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
border_depth = element . BorderWidth
if btype != BUTTON_TYPE_REALTIME :
2018-08-27 02:16:54 +00:00
tkbutton = tk . Button ( tk_row_frame , text = btext , width = width , height = height , command = element . ButtonCallBack , justify = tk . LEFT , bd = border_depth , font = font )
2018-08-14 20:49:36 +00:00
else :
2018-08-27 02:16:54 +00:00
tkbutton = tk . Button ( tk_row_frame , text = btext , width = width , height = height , justify = tk . LEFT , bd = border_depth , font = font )
2018-08-14 20:49:36 +00:00
tkbutton . bind ( ' <ButtonRelease-1> ' , element . ButtonReleaseCallBack )
tkbutton . bind ( ' <ButtonPress-1> ' , element . ButtonPressCallBack )
if bc != ( None , None ) and bc != COLOR_SYSTEM_DEFAULT :
tkbutton . config ( foreground = bc [ 0 ] , background = bc [ 1 ] )
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
2018-08-18 20:55:21 +00:00
tkbutton . config ( highlightthickness = 0 )
2018-08-14 20:49:36 +00:00
photo = tk . PhotoImage ( file = element . ImageFilename )
if element . ImageSize != ( None , None ) :
width , height = element . ImageSize
if element . ImageSubsample :
photo = photo . subsample ( element . ImageSubsample )
else :
width , height = photo . width ( ) , photo . height ( )
tkbutton . config ( image = photo , width = width , height = height )
tkbutton . image = photo
2018-08-18 20:55:21 +00:00
if width != 0 :
2018-08-27 02:16:54 +00:00
tkbutton . configure ( wraplength = wraplen + 10 ) # set wrap to width of widget
2018-08-14 20:49:36 +00:00
tkbutton . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
if element . Focus is True or ( toplevel_form . UseDefaultFocus and not focus_set ) :
focus_set = True
element . TKButton . bind ( ' <Return> ' , element . ReturnKeyHandler )
element . TKButton . focus_set ( )
toplevel_form . TKroot . focus_force ( )
# ------------------------- INPUT (Single Line) element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_TEXT :
default_text = element . DefaultText
element . TKStringVar = tk . StringVar ( )
element . TKStringVar . set ( default_text )
show = element . PasswordCharacter if element . PasswordCharacter else " "
element . TKEntry = tk . Entry ( tk_row_frame , width = element_size [ 0 ] , textvariable = element . TKStringVar , bd = border_depth , font = font , show = show )
element . TKEntry . bind ( ' <Return> ' , element . ReturnKeyHandler )
if element . BackgroundColor is not None and element . BackgroundColor != COLOR_SYSTEM_DEFAULT :
element . TKEntry . configure ( background = element . BackgroundColor )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
element . TKEntry . configure ( fg = text_color )
element . TKEntry . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
if element . Focus is True or ( toplevel_form . UseDefaultFocus and not focus_set ) :
focus_set = True
element . TKEntry . focus_set ( )
# ------------------------- COMBO BOX (Drop Down) element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_COMBO :
max_line_len = max ( [ len ( str ( l ) ) for l in element . Values ] )
if auto_size_text is False : width = element_size [ 0 ]
else : width = max_line_len
element . TKStringVar = tk . StringVar ( )
if element . BackgroundColor and element . BackgroundColor != COLOR_SYSTEM_DEFAULT :
combostyle = ttk . Style ( )
try :
combostyle . theme_create ( ' combostyle ' ,
settings = { ' TCombobox ' :
{ ' configure ' :
{ ' selectbackground ' : element . BackgroundColor ,
' fieldbackground ' : element . BackgroundColor ,
' foreground ' : text_color ,
' background ' : element . BackgroundColor }
} } )
except :
try :
combostyle . theme_settings ( ' combostyle ' ,
settings = { ' TCombobox ' :
{ ' configure ' :
{ ' selectbackground ' : element . BackgroundColor ,
' fieldbackground ' : element . BackgroundColor ,
' foreground ' : text_color ,
' background ' : element . BackgroundColor }
} } )
except : pass
# ATTENTION: this applies the new style 'combostyle' to all ttk.Combobox
combostyle . theme_use ( ' combostyle ' )
element . TKCombo = ttk . Combobox ( tk_row_frame , width = width , textvariable = element . TKStringVar , font = font )
# element.TKCombo['state']='readonly'
element . TKCombo [ ' values ' ] = element . Values
# if element.BackgroundColor is not None:
# element.TKCombo.configure(background=element.BackgroundColor)
element . TKCombo . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
element . TKCombo . current ( 0 )
# ------------------------- LISTBOX element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_LISTBOX :
max_line_len = max ( [ len ( str ( l ) ) for l in element . Values ] )
if auto_size_text is False : width = element_size [ 0 ]
else : width = max_line_len
2018-08-18 20:55:21 +00:00
listbox_frame = tk . Frame ( tk_row_frame )
2018-08-14 20:49:36 +00:00
element . TKStringVar = tk . StringVar ( )
2018-08-18 20:55:21 +00:00
element . TKListbox = tk . Listbox ( listbox_frame , height = element_size [ 1 ] , width = width , selectmode = element . SelectMode , font = font )
2018-08-14 20:49:36 +00:00
for item in element . Values :
element . TKListbox . insert ( tk . END , item )
element . TKListbox . selection_set ( 0 , 0 )
if element . BackgroundColor is not None and element . BackgroundColor != COLOR_SYSTEM_DEFAULT :
element . TKListbox . configure ( background = element . BackgroundColor )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
element . TKListbox . configure ( fg = text_color )
2018-08-28 03:57:52 +00:00
if element . SelectSubmits :
element . TKListbox . bind ( ' <<ListboxSelect>> ' , element . ListboxSelectHandler )
2018-08-18 20:55:21 +00:00
vsb = tk . Scrollbar ( listbox_frame , orient = " vertical " , command = element . TKListbox . yview )
element . TKListbox . configure ( yscrollcommand = vsb . set )
element . TKListbox . pack ( side = tk . LEFT )
vsb . pack ( side = tk . LEFT , fill = ' y ' )
listbox_frame . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-14 20:49:36 +00:00
# ------------------------- INPUT MULTI LINE element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_MULTILINE :
default_text = element . DefaultText
width , height = element_size
element . TKText = tk . scrolledtext . ScrolledText ( tk_row_frame , width = width , height = height , wrap = ' word ' , bd = border_depth , font = font )
element . TKText . insert ( 1.0 , default_text ) # set the default text
if element . BackgroundColor is not None and element . BackgroundColor != COLOR_SYSTEM_DEFAULT :
element . TKText . configure ( background = element . BackgroundColor )
element . TKText . vbar . config ( troughcolor = DEFAULT_SCROLLBAR_COLOR )
element . TKText . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
if element . EnterSubmits :
element . TKText . bind ( ' <Return> ' , element . ReturnKeyHandler )
if element . Focus is True or ( toplevel_form . UseDefaultFocus and not focus_set ) :
focus_set = True
element . TKText . focus_set ( )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
element . TKText . configure ( fg = text_color )
# ------------------------- INPUT CHECKBOX element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_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 = tk . Checkbutton ( tk_row_frame , anchor = tk . NW , text = element . Text , width = width , variable = element . TKIntVar , bd = border_depth , font = font )
if default_value is None :
element . TKCheckbutton . configure ( state = ' disable ' )
if element . BackgroundColor is not None :
element . TKCheckbutton . configure ( background = element . BackgroundColor )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
element . TKCheckbutton . configure ( fg = text_color )
element . TKCheckbutton . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
# ------------------------- PROGRESS BAR element ------------------------- #
elif element_type == ELEM_TYPE_PROGRESS_BAR :
# save this form because it must be 'updated' (refreshed) solely for the purpose of updating bar
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
element . TKProgressBar = TKProgressBar ( tk_row_frame , element . MaxValue , progress_length , progress_width , orientation = direction , BarColor = bar_color , border_width = element . BorderWidth , relief = element . Relief , style = element . BarStyle )
element . TKProgressBar . TKProgressBarForReal . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
# ------------------------- INPUT RADIO BUTTON element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_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 ( row_num , col_num ) # value to set intvar to if this radio is selected
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 = 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 . BackgroundColor is not None :
element . TKRadio . configure ( background = element . BackgroundColor )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
element . TKRadio . configure ( fg = text_color )
element . TKRadio . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
# ------------------------- INPUT SPIN Box element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_SPIN :
width , height = element_size
width = 0 if auto_size_text else element_size [ 0 ]
element . TKStringVar = tk . StringVar ( )
element . TKSpinBox = tk . Spinbox ( tk_row_frame , values = element . Values , textvariable = element . TKStringVar , width = width , bd = border_depth )
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 . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
element . TKSpinBox . configure ( fg = text_color )
# ------------------------- OUTPUT element ------------------------- #
elif element_type == ELEM_TYPE_OUTPUT :
width , height = element_size
2018-08-28 16:25:15 +00:00
element . TKOut = TKOutput ( tk_row_frame , width = width , height = height , bd = border_depth , background_color = element . BackgroundColor , text_color = text_color , font = font , pad = element . Pad )
2018-08-26 19:20:54 +00:00
element . TKOut . pack ( side = tk . LEFT )
2018-08-14 20:49:36 +00:00
# ------------------------- IMAGE Box element ------------------------- #
elif element_type == ELEM_TYPE_IMAGE :
2018-08-19 03:19:11 +00:00
if element . Filename is not None :
photo = tk . PhotoImage ( file = element . Filename )
elif element . Data is not None :
photo = tk . PhotoImage ( data = element . Data )
2018-08-14 20:49:36 +00:00
else :
2018-08-19 03:19:11 +00:00
photo = None
print ( ' *ERROR laying out form.... Image Element has no image specified* ' )
if photo is not None :
if element_size == ( None , None ) or element_size == 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 = border_depth )
element . tktext_label . image = photo
# tktext_label.configure(anchor=tk.NW, image=photo)
element . tktext_label . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-26 19:20:54 +00:00
# ------------------------- Canvas element ------------------------- #
elif element_type == ELEM_TYPE_CANVAS :
width , height = element_size
2018-08-27 02:16:54 +00:00
if element . TKCanvas is None :
element . TKCanvas = tk . Canvas ( tk_row_frame , width = width , height = height , bd = border_depth )
2018-08-28 03:57:52 +00:00
else :
element . TKCanvas . master = tk_row_frame
2018-08-26 19:20:54 +00:00
if element . BackgroundColor is not None and element . BackgroundColor != COLOR_SYSTEM_DEFAULT :
element . TKCanvas . configure ( background = element . BackgroundColor )
element . TKCanvas . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-27 02:16:54 +00:00
# ------------------------- Frame element ------------------------- #
elif element_type == ELEM_TYPE_FRAME :
width , height = element_size
if element . TKFrame is None :
element . TKFrame = tk . Frame ( tk_row_frame , width = width , height = height , bd = border_depth )
if element . BackgroundColor is not None and element . BackgroundColor != COLOR_SYSTEM_DEFAULT :
element . TKFrame . configure ( background = element . BackgroundColor )
element . TKFrame . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-14 20:49:36 +00:00
# ------------------------- SLIDER Box element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_SLIDER :
slider_length = element_size [ 0 ] * CharWidthInPixels ( )
slider_width = element_size [ 1 ]
element . TKIntVar = tk . IntVar ( )
element . TKIntVar . set ( element . DefaultValue )
if element . Orientation [ 0 ] == ' 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 ]
2018-08-27 15:20:01 +00:00
tkscale = 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 )
2018-08-14 20:49:36 +00:00
# tktext_label.configure(anchor=tk.NW, image=photo)
2018-08-18 20:55:21 +00:00
tkscale . config ( highlightthickness = 0 )
2018-08-14 20:49:36 +00:00
if element . BackgroundColor is not None :
tkscale . configure ( background = element . BackgroundColor )
tkscale . config ( troughcolor = DEFAULT_SCROLLBAR_COLOR )
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT :
tkscale . configure ( fg = text_color )
2018-08-18 20:55:21 +00:00
tkscale . pack ( side = tk . LEFT , padx = element . Pad [ 0 ] , pady = element . Pad [ 1 ] )
2018-08-25 02:47:21 +00:00
element . TKScale = tkscale
2018-08-14 20:49:36 +00:00
#............................DONE WITH ROW pack the row of widgets ..........................#
# done with row, pack the row of widgets
tk_row_frame . grid ( row = row_num + 2 , sticky = tk . NW , padx = DEFAULT_MARGINS [ 0 ] )
2018-08-29 00:07:17 +00:00
if form . BackgroundColor is not None and form . BackgroundColor != COLOR_SYSTEM_DEFAULT :
2018-08-14 20:49:36 +00:00
tk_row_frame . configure ( background = form . BackgroundColor )
if not toplevel_form . IsTabbedForm :
toplevel_form . TKroot . configure ( padx = DEFAULT_MARGINS [ 0 ] , pady = DEFAULT_MARGINS [ 1 ] )
else : toplevel_form . ParentWindow . configure ( padx = DEFAULT_MARGINS [ 0 ] , pady = DEFAULT_MARGINS [ 1 ] )
return
2018-07-11 19:19:24 +00:00
def ConvertFlexToTK ( MyFlexForm ) :
2018-08-14 20:49:36 +00:00
master = MyFlexForm . TKroot
# only set title on non-tabbed forms
if not MyFlexForm . IsTabbedForm :
master . title ( MyFlexForm . Title )
InitializeResults ( MyFlexForm )
PackFormIntoFrame ( MyFlexForm , master , MyFlexForm )
#....................................... DONE creating and laying out window ..........................#
if MyFlexForm . IsTabbedForm :
master = MyFlexForm . ParentWindow
master . attributes ( ' -alpha ' , 0 ) # hide window while getting info and moving
screen_width = master . winfo_screenwidth ( ) # get window info to move to middle of screen
screen_height = master . winfo_screenheight ( )
if MyFlexForm . Location != ( None , None ) :
x , y = MyFlexForm . Location
elif DEFAULT_WINDOW_LOCATION != ( None , None ) :
x , y = DEFAULT_WINDOW_LOCATION
else :
master . update_idletasks ( ) # don't forget
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
move_string = ' + %i + %i ' % ( int ( x ) , int ( y ) )
master . geometry ( move_string )
master . attributes ( ' -alpha ' , 255 ) # Make window visible again
master . update_idletasks ( ) # don't forget
return
2018-07-11 19:19:24 +00:00
# ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----#
2018-07-16 18:52:16 +00:00
def ShowTabbedForm ( title , * args , auto_close = False , auto_close_duration = DEFAULT_AUTOCLOSE_TIME , fav_icon = DEFAULT_WINDOW_ICON ) :
2018-08-14 20:49:36 +00:00
# takes as input (form, rows, tab name) for each tab
2018-07-11 19:19:24 +00:00
global _my_windows
uber = UberForm ( )
root = tk . Tk ( )
uber . TKroot = root
2018-07-16 18:52:16 +00:00
if title is not None :
root . title ( title )
2018-07-11 19:19:24 +00:00
if not len ( args ) :
2018-07-30 21:00:42 +00:00
print ( ' ******************* SHOW TABBED FORMS ERROR .... no arguments ' )
2018-07-11 19:19:24 +00:00
return
2018-07-26 23:53:17 +00:00
if DEFAULT_BACKGROUND_COLOR :
framestyle = ttk . Style ( )
try :
framestyle . theme_create ( ' framestyle ' , parent = ' alt ' ,
settings = { ' TFrame ' :
{ ' configure ' :
{ ' background ' : DEFAULT_BACKGROUND_COLOR ,
} } } )
except : pass
# ATTENTION: this applies the new style 'combostyle' to all ttk.Combobox
# framestyle.theme_use('framestyle')
2018-07-11 19:19:24 +00:00
tab_control = ttk . Notebook ( root )
for num , x in enumerate ( args ) :
form , rows , tab_name = x
form . AddRows ( rows )
2018-07-26 23:53:17 +00:00
if DEFAULT_BACKGROUND_COLOR :
framestyle . theme_use ( ' framestyle ' )
2018-07-11 19:19:24 +00:00
tab = ttk . Frame ( tab_control ) # Create tab 1
2018-07-26 23:53:17 +00:00
# s.configure("my.Frame.TFrame", background=DEFAULT_BACKGROUND_COLOR)
2018-07-11 19:19:24 +00:00
tab_control . add ( tab , text = tab_name ) # Add tab 1
# tab_control.configure(text='new text')
tab_control . grid ( row = 0 , sticky = tk . W )
form . TKTabControl = tab_control
form . TKroot = tab
form . IsTabbedForm = True
form . ParentWindow = root
ConvertFlexToTK ( form )
form . UberParent = uber
uber . AddForm ( form )
uber . FormReturnValues . append ( form . ReturnValues )
# dangerous?? or clever? use the final form as a callback for autoclose
2018-08-06 16:20:18 +00:00
id = root . after ( auto_close_duration * 1000 , form . _AutoCloseAlarmCallback ) if auto_close else 0
2018-07-16 18:52:16 +00:00
icon = fav_icon if not _my_windows . user_defined_icon else _my_windows . user_defined_icon
2018-07-11 19:19:24 +00:00
try : uber . TKroot . iconbitmap ( icon )
except : pass
root . mainloop ( )
if id : root . after_cancel ( id )
uber . TKrootDestroyed = True
return uber . FormReturnValues
# ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----#
2018-07-16 18:52:16 +00:00
def StartupTK ( my_flex_form ) :
2018-07-11 19:19:24 +00:00
global _my_windows
ow = _my_windows . NumOpenWindows
2018-08-16 13:21:30 +00:00
# print('Starting TK open Windows = {}'.format(ow))
2018-07-11 19:19:24 +00:00
root = tk . Tk ( ) if not ow else tk . Toplevel ( )
2018-08-29 00:07:17 +00:00
if my_flex_form . BackgroundColor is not None and my_flex_form . BackgroundColor != COLOR_SYSTEM_DEFAULT :
2018-07-26 23:53:17 +00:00
root . configure ( background = my_flex_form . BackgroundColor )
2018-08-18 20:55:21 +00:00
_my_windows . Increment ( )
2018-07-11 19:19:24 +00:00
2018-07-16 18:52:16 +00:00
my_flex_form . TKroot = root
2018-07-11 19:19:24 +00:00
# root.protocol("WM_DELETE_WINDOW", MyFlexForm.DestroyedCallback())
# root.bind('<Destroy>', MyFlexForm.DestroyedCallback())
2018-07-16 18:52:16 +00:00
ConvertFlexToTK ( my_flex_form )
my_flex_form . SetIcon ( my_flex_form . WindowIcon )
2018-08-20 19:27:12 +00:00
if my_flex_form . ReturnKeyboardEvents and not my_flex_form . NonBlocking :
2018-08-20 17:48:02 +00:00
root . bind ( " <KeyRelease> " , my_flex_form . KeyboardCallback )
2018-08-21 03:45:09 +00:00
root . bind ( " <MouseWheel> " , my_flex_form . MouseWheelCallback )
2018-08-20 19:27:12 +00:00
elif my_flex_form . ReturnKeyboardEvents :
root . bind ( " <Key> " , my_flex_form . KeyboardCallback )
2018-08-21 03:45:09 +00:00
root . bind ( " <MouseWheel> " , my_flex_form . MouseWheelCallback )
2018-07-16 18:52:16 +00:00
if my_flex_form . AutoClose :
duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form . AutoCloseDuration is None else my_flex_form . AutoCloseDuration
2018-08-06 16:20:18 +00:00
my_flex_form . TKAfterID = root . after ( duration * 1000 , my_flex_form . _AutoCloseAlarmCallback )
2018-07-16 18:52:16 +00:00
if my_flex_form . NonBlocking :
my_flex_form . TKroot . protocol ( " WM_WINDOW_DESTROYED " , my_flex_form . OnClosingCallback ( ) )
2018-07-11 19:19:24 +00:00
else : # it's a blocking form
2018-08-16 13:21:30 +00:00
# print('..... CALLING MainLoop')
2018-07-16 18:52:16 +00:00
my_flex_form . TKroot . mainloop ( )
2018-08-16 13:21:30 +00:00
# print('..... BACK from MainLoop')
2018-08-14 20:49:36 +00:00
if not my_flex_form . FormRemainedOpen :
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-07-16 18:52:16 +00:00
if my_flex_form . RootNeedsDestroying :
my_flex_form . TKroot . destroy ( )
my_flex_form . RootNeedsDestroying = False
2018-07-11 19:19:24 +00:00
return
# ==============================_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 ' )
num_lines = len ( lines ) # number of original lines of text
max_line_len = max ( [ len ( l ) for l in lines ] ) # longest line
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
# ------------------------------------------------------------------------------------------------------------------ #
# ===================================== Upper PySimpleGUI ============================================================== #
# Pre-built dialog boxes for all your needs #
# ------------------------------------------------------------------------------------------------------------------ #
# ==================================== MSG BOX =====#
# Display a message wrapping at 60 characters #
# Exits via an OK button2 press #
# Returns nothing #
# ===================================================#
2018-07-18 19:11:22 +00:00
def MsgBox ( * args , button_color = None , button_type = MSG_BOX_OK , auto_close = False , auto_close_duration = None , icon = DEFAULT_WINDOW_ICON , line_width = None , font = None ) :
2018-07-11 19:19:24 +00:00
'''
2018-07-13 14:53:50 +00:00
Show message box . Displays one line per user supplied argument . Takes any Type of variable to display .
2018-07-11 19:19:24 +00:00
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param button_type :
: param auto_close :
: param auto_close_duration :
: param icon :
: param line_width :
: param font :
2018-07-11 19:19:24 +00:00
: return :
'''
2018-07-13 14:53:50 +00:00
if not args :
args_to_print = [ ' ' ]
else :
args_to_print = args
2018-07-18 19:11:22 +00:00
if line_width != None :
local_line_width = line_width
else :
local_line_width = MESSAGE_BOX_LINE_WIDTH
2018-08-14 20:49:36 +00:00
title = args_to_print [ 0 ] if args_to_print [ 0 ] is not None else ' None '
with FlexForm ( title , auto_size_text = True , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , icon = icon , font = font ) as form :
2018-07-11 19:19:24 +00:00
max_line_total , total_lines = 0 , 0
2018-07-13 14:53:50 +00:00
for message in args_to_print :
2018-07-11 19:19:24 +00:00
# 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 )
2018-07-15 23:21:06 +00:00
if message . count ( ' \n ' ) :
message_wrapped = message
else :
2018-07-18 19:11:22 +00:00
message_wrapped = textwrap . fill ( message , local_line_width )
2018-07-11 19:19:24 +00:00
message_wrapped_lines = message_wrapped . count ( ' \n ' ) + 1
longest_line_len = max ( [ len ( l ) for l in message . split ( ' \n ' ) ] )
2018-07-18 19:11:22 +00:00
width_used = min ( longest_line_len , local_line_width )
2018-07-11 19:19:24 +00:00
max_line_total = max ( max_line_total , width_used )
# height = _GetNumLinesNeeded(message, width_used)
height = message_wrapped_lines
2018-08-24 11:45:31 +00:00
# print('Msgbox width, height', width_used, height)
2018-08-26 19:20:54 +00:00
form . AddRow ( Text ( message_wrapped , auto_size_text = True ) )
2018-07-11 19:19:24 +00:00
total_lines + = height
pad = max_line_total - 15 if max_line_total > 15 else 1
pad = 1
# show either an OK or Yes/No depending on paramater
2018-07-16 18:52:16 +00:00
if button_type is MSG_BOX_YES_NO :
2018-08-09 13:35:21 +00:00
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , Yes ( button_color = button_color , focus = True , bind_return_key = True ) , No (
2018-07-16 18:52:16 +00:00
button_color = button_color ) )
2018-07-11 19:19:24 +00:00
( button_text , values ) = form . Show ( )
return button_text == ' Yes '
2018-07-16 18:52:16 +00:00
elif button_type is MSG_BOX_CANCELLED :
2018-08-09 13:35:21 +00:00
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , SimpleButton ( ' Cancelled ' , button_color = button_color , focus = True , bind_return_key = True ) )
2018-07-16 18:52:16 +00:00
elif button_type is MSG_BOX_ERROR :
2018-08-09 13:35:21 +00:00
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , SimpleButton ( ' ERROR ' , size = ( 5 , 1 ) , button_color = button_color , focus = True , bind_return_key = True ) )
2018-07-16 18:52:16 +00:00
elif button_type is MSG_BOX_OK_CANCEL :
2018-08-09 13:35:21 +00:00
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , SimpleButton ( ' OK ' , size = ( 5 , 1 ) , button_color = button_color , focus = True , bind_return_key = True ) ,
2018-07-16 18:52:16 +00:00
SimpleButton ( ' Cancel ' , size = ( 5 , 1 ) , button_color = button_color ) )
2018-07-11 19:19:24 +00:00
else :
2018-08-09 13:35:21 +00:00
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , SimpleButton ( ' OK ' , size = ( 5 , 1 ) , button_color = button_color , focus = True , bind_return_key = True ) )
2018-07-11 19:19:24 +00:00
button , values = form . Show ( )
return button
# ============================== MsgBoxAutoClose====#
# Lazy function. Same as calling MsgBox with parms #
# ===================================================#
2018-07-16 18:52:16 +00:00
def MsgBoxAutoClose ( * args , button_color = None , auto_close = True , auto_close_duration = DEFAULT_AUTOCLOSE_TIME , font = None ) :
2018-07-13 14:53:50 +00:00
'''
Display a standard MsgBox that will automatically close after a specified amount of time
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param auto_close :
: param auto_close_duration :
: param font :
2018-07-13 14:53:50 +00:00
: return :
'''
2018-07-16 18:52:16 +00:00
MsgBox ( * args , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , font = font )
2018-07-11 19:19:24 +00:00
return
# ============================== MsgBoxError =====#
# Like MsgBox but presents RED BUTTONS #
# ===================================================#
2018-07-16 22:36:17 +00:00
def MsgBoxError ( * args , button_color = DEFAULT_ERROR_BUTTON_COLOR , auto_close = False , auto_close_duration = None , font = None ) :
2018-07-13 14:53:50 +00:00
'''
Display a MsgBox with a red button
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param auto_close :
: param auto_close_duration :
2018-07-16 22:36:17 +00:00
: param font :
2018-07-13 14:53:50 +00:00
: return :
'''
2018-07-16 22:36:17 +00:00
MsgBox ( * args , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , font = font )
2018-07-11 19:19:24 +00:00
return
# ============================== MsgBoxCancel =====#
2018-07-13 14:53:50 +00:00
# #
2018-07-11 19:19:24 +00:00
# ===================================================#
2018-08-06 16:20:18 +00:00
def MsgBoxCancel ( * args , button_color = None , auto_close = False , auto_close_duration = None , font = None ) :
2018-07-13 14:53:50 +00:00
'''
Display a MsgBox with a single " Cancel " button .
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param auto_close :
: param auto_close_duration :
: param font :
2018-07-13 14:53:50 +00:00
: return :
'''
2018-07-16 18:52:16 +00:00
MsgBox ( * args , button_type = MSG_BOX_CANCELLED , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , font = font )
2018-07-11 19:19:24 +00:00
return
# ============================== MsgBoxOK =====#
# Like MsgBox but only 1 button #
# ===================================================#
2018-08-06 16:20:18 +00:00
def MsgBoxOK ( * args , button_color = None , auto_close = False , auto_close_duration = None , font = None ) :
2018-07-13 14:53:50 +00:00
'''
Display a MsgBox with a single buttoned labelled " OK "
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param auto_close :
: param auto_close_duration :
: param font :
2018-07-13 14:53:50 +00:00
: return :
'''
2018-07-16 18:52:16 +00:00
MsgBox ( * args , button_type = MSG_BOX_OK , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , font = font )
2018-07-11 19:19:24 +00:00
return
2018-07-13 14:53:50 +00:00
# ============================== MsgBoxOKCancel ====#
# Like MsgBox but presents OK and Cancel buttons #
2018-07-11 19:19:24 +00:00
# ===================================================#
2018-07-16 18:52:16 +00:00
def MsgBoxOKCancel ( * args , button_color = None , auto_close = False , auto_close_duration = None , font = None ) :
2018-07-13 14:53:50 +00:00
'''
Display MsgBox with 2 buttons , " OK " and " Cancel "
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param auto_close :
: param auto_close_duration :
: param font :
2018-07-13 14:53:50 +00:00
: return :
'''
2018-07-16 18:52:16 +00:00
result = MsgBox ( * args , button_type = MSG_BOX_OK_CANCEL , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , font = font )
2018-07-11 19:19:24 +00:00
return result
# ==================================== YesNoBox=====#
# Like MsgBox but presents Yes and No buttons #
# Returns True if Yes was pressed else False #
# ===================================================#
2018-07-16 18:52:16 +00:00
def MsgBoxYesNo ( * args , button_color = None , auto_close = False , auto_close_duration = None , font = None ) :
2018-07-13 14:53:50 +00:00
'''
Display MsgBox with 2 buttons , " Yes " and " No "
: param args :
2018-07-16 18:52:16 +00:00
: param button_color :
: param auto_close :
: param auto_close_duration :
: param font :
2018-07-13 14:53:50 +00:00
: return :
'''
2018-07-16 18:52:16 +00:00
result = MsgBox ( * args , button_type = MSG_BOX_YES_NO , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration , font = font )
2018-07-11 19:19:24 +00:00
return result
# ============================== PROGRESS METER ========================================== #
def ConvertArgsToSingleString ( * 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 ( l ) for l in message . split ( ' \n ' ) ] )
2018-07-26 23:53:17 +00:00
width_used = max ( longest_line_len , width_used )
2018-07-11 19:19:24 +00:00
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
# ============================== ProgressMeter =====#
# ===================================================#
2018-08-12 21:45:12 +00:00
def _ProgressMeter ( title , max_value , * args , orientation = None , bar_color = ( None , None ) , button_color = None , size = DEFAULT_PROGRESS_BAR_SIZE , scale = ( None , None ) , border_width = None ) :
2018-07-11 19:19:24 +00:00
'''
Create and show a form on tbe caller ' s behalf.
2018-07-16 18:52:16 +00:00
: param title :
: param max_value :
2018-07-11 19:19:24 +00:00
: param args : ANY number of arguments the caller wants to display
2018-07-18 19:11:22 +00:00
: param orientation :
2018-07-16 18:52:16 +00:00
: param bar_color :
: param size :
: param scale :
2018-07-11 19:19:24 +00:00
: param Style :
: param StyleOffset :
: return : ProgressBar object that is in the form
'''
2018-07-18 19:11:22 +00:00
local_orientation = DEFAULT_METER_ORIENTATION if orientation is None else orientation
local_border_width = DEFAULT_PROGRESS_BAR_BORDER_WIDTH if border_width is None else border_width
2018-08-12 21:45:12 +00:00
bar2 = ProgressBar ( max_value , orientation = local_orientation , size = size , bar_color = bar_color , scale = scale , border_width = local_border_width , relief = DEFAULT_PROGRESS_BAR_RELIEF )
2018-07-16 18:52:16 +00:00
form = FlexForm ( title , auto_size_text = True )
2018-07-11 19:19:24 +00:00
# Form using a horizontal bar
2018-07-18 19:11:22 +00:00
if local_orientation [ 0 ] . lower ( ) == ' h ' :
2018-07-11 19:19:24 +00:00
single_line_message , width , height = ConvertArgsToSingleString ( * args )
bar2 . TextToDisplay = single_line_message
2018-07-16 18:52:16 +00:00
bar2 . MaxValue = max_value
2018-07-11 19:19:24 +00:00
bar2 . CurrentValue = 0
2018-08-12 21:45:12 +00:00
bar_text = Text ( single_line_message , size = ( width , height + 3 ) , auto_size_text = True )
form . AddRow ( bar_text )
2018-07-11 19:19:24 +00:00
form . AddRow ( ( bar2 ) )
2018-07-16 18:52:16 +00:00
form . AddRow ( ( Cancel ( button_color = button_color ) ) )
2018-07-11 19:19:24 +00:00
else :
single_line_message , width , height = ConvertArgsToSingleString ( * args )
bar2 . TextToDisplay = single_line_message
2018-07-16 18:52:16 +00:00
bar2 . MaxValue = max_value
2018-07-11 19:19:24 +00:00
bar2 . CurrentValue = 0
2018-08-12 21:45:12 +00:00
bar_text = Text ( single_line_message , size = ( width , height + 3 ) , auto_size_text = True )
form . AddRow ( bar2 , bar_text )
2018-07-16 18:52:16 +00:00
form . AddRow ( ( Cancel ( button_color = button_color ) ) )
2018-07-11 19:19:24 +00:00
form . NonBlocking = True
2018-07-16 18:52:16 +00:00
form . Show ( non_blocking = True )
2018-08-12 21:45:12 +00:00
return bar2 , bar_text
2018-07-11 19:19:24 +00:00
# ============================== ProgressMeterUpdate =====#
2018-08-12 21:45:12 +00:00
def _ProgressMeterUpdate ( bar , value , text_elem , * args ) :
2018-07-11 19:19:24 +00:00
'''
Update the progress meter for a form
: param form : class ProgressBar
2018-07-16 18:52:16 +00:00
: param value : int
2018-07-11 19:19:24 +00:00
: return : True if not cancelled , OK . . . . False if Error
'''
global _my_windows
if bar == None : return False
if bar . BarExpired : return False
message , w , h = ConvertArgsToSingleString ( * args )
2018-08-12 21:45:12 +00:00
text_elem . Update ( message )
# bar.TextToDisplay = message
2018-07-16 18:52:16 +00:00
bar . CurrentValue = value
rc = bar . UpdateBar ( value )
if value > = bar . MaxValue or not rc :
2018-07-11 19:19:24 +00:00
bar . BarExpired = True
2018-08-06 16:20:18 +00:00
bar . ParentForm . _Close ( )
2018-07-11 19:19:24 +00:00
if bar . ParentForm . RootNeedsDestroying :
try :
2018-07-23 19:14:34 +00:00
bar . ParentForm . TKroot . destroy ( )
2018-08-18 20:55:21 +00:00
_my_windows . Decrement ( )
2018-07-11 19:19:24 +00:00
except : pass
bar . ParentForm . RootNeedsDestroying = False
bar . ParentForm . __del__ ( )
return False
return rc
# ============================== EASY PROGRESS METER ========================================== #
# class to hold the easy meter info (a global variable essentialy)
class EasyProgressMeterDataClass ( ) :
2018-07-16 18:52:16 +00:00
def __init__ ( self , title = ' ' , current_value = 1 , max_value = 10 , start_time = None , stat_messages = ( ) ) :
self . Title = title
self . CurrentValue = current_value
self . MaxValue = max_value
self . StartTime = start_time
self . StatMessages = stat_messages
2018-07-11 19:19:24 +00:00
self . ParentForm = None
self . MeterID = None
2018-08-12 21:45:12 +00:00
self . MeterText = None
2018-07-11 19:19:24 +00:00
# =========================== COMPUTE PROGRESS STATS ======================#
def ComputeProgressStats ( self ) :
utc = datetime . datetime . utcnow ( )
time_delta = utc - self . StartTime
total_seconds = time_delta . total_seconds ( )
if not total_seconds :
total_seconds = 1
try :
time_per_item = total_seconds / self . CurrentValue
except :
time_per_item = 1
seconds_remaining = ( self . MaxValue - self . CurrentValue ) * 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 . StatMessages = [
' {} of {} ' . format ( self . CurrentValue , self . MaxValue ) ,
' {} % ' . format ( 100 * self . CurrentValue / / self . MaxValue ) ,
' ' ,
' {:6.2f} Iterations per Second ' . format ( self . CurrentValue / total_seconds ) ,
' {:6.2f} Seconds per Iteration ' . format ( total_seconds / ( self . CurrentValue if self . CurrentValue else 1 ) ) ,
' ' ,
' {} Elapsed Time ' . format ( time_delta_short ) ,
' {} Time Remaining ' . format ( time_remaining_short ) ,
' {} Estimated Total Time ' . format ( total_time_short ) ]
return
# ============================== EasyProgressMeter =====#
2018-07-27 19:45:38 +00:00
def EasyProgressMeter ( title , current_value , max_value , * args , orientation = None , bar_color = ( None , None ) , button_color = None , size = DEFAULT_PROGRESS_BAR_SIZE , scale = ( None , None ) , border_width = None ) :
2018-07-11 19:19:24 +00:00
'''
A ONE - LINE progress meter . Add to your code where ever you need a meter . No need for a second
function call before your loop . You ' ve got enough code to write!
2018-07-16 18:52:16 +00:00
: param title : Title will be shown on the window
: param current_value : Current count of your items
: param max_value : Max value your count will ever reach . This indicates it should be closed
2018-07-11 19:19:24 +00:00
: param args : VARIABLE number of arguements . . . you request it , we ' ll print it no matter what the item!
2018-07-16 18:52:16 +00:00
: param orientation :
: param bar_color :
: param size :
: param scale :
2018-07-11 19:19:24 +00:00
: param Style :
: param StyleOffset :
: return : False if should stop the meter
'''
2018-07-18 19:11:22 +00:00
local_border_width = DEFAULT_PROGRESS_BAR_BORDER_WIDTH if not border_width else border_width
2018-07-11 19:19:24 +00:00
# STATIC VARIABLE!
# This is a very clever form of static variable using a function attribute
# If the variable doesn't yet exist, then it will create it and initialize with the 3rd parameter
EasyProgressMeter . EasyProgressMeterData = getattr ( EasyProgressMeter , ' EasyProgressMeterData ' , EasyProgressMeterDataClass ( ) )
# if no meter currently running
if EasyProgressMeter . EasyProgressMeterData . MeterID is None : # Starting a new meter
2018-07-16 18:52:16 +00:00
if int ( current_value ) > = int ( max_value ) :
2018-07-11 19:19:24 +00:00
return False
del ( EasyProgressMeter . EasyProgressMeterData )
2018-07-16 18:52:16 +00:00
EasyProgressMeter . EasyProgressMeterData = EasyProgressMeterDataClass ( title , 1 , int ( max_value ) , datetime . datetime . utcnow ( ) , [ ] )
2018-07-11 19:19:24 +00:00
EasyProgressMeter . EasyProgressMeterData . ComputeProgressStats ( )
message = " \n " . join ( [ line for line in EasyProgressMeter . EasyProgressMeterData . StatMessages ] )
2018-08-12 21:45:12 +00:00
EasyProgressMeter . EasyProgressMeterData . MeterID , EasyProgressMeter . EasyProgressMeterData . MeterText = _ProgressMeter ( title , int ( max_value ) , message , * args , orientation = orientation , bar_color = bar_color , size = size , scale = scale , button_color = button_color , border_width = local_border_width )
2018-07-11 19:19:24 +00:00
EasyProgressMeter . EasyProgressMeterData . ParentForm = EasyProgressMeter . EasyProgressMeterData . MeterID . ParentForm
return True
# if exactly the same values as before, then ignore.
2018-07-16 18:52:16 +00:00
if EasyProgressMeter . EasyProgressMeterData . MaxValue == max_value and EasyProgressMeter . EasyProgressMeterData . CurrentValue == current_value :
2018-07-11 19:19:24 +00:00
return True
2018-07-16 18:52:16 +00:00
if EasyProgressMeter . EasyProgressMeterData . MaxValue != int ( max_value ) :
2018-07-11 19:19:24 +00:00
EasyProgressMeter . EasyProgressMeterData . MeterID = None
EasyProgressMeter . EasyProgressMeterData . ParentForm = None
del ( EasyProgressMeter . EasyProgressMeterData )
EasyProgressMeter . EasyProgressMeterData = EasyProgressMeterDataClass ( ) # setup a new progress meter
return True # HAVE to return TRUE or else the new meter will thing IT is failing when it hasn't
2018-07-16 18:52:16 +00:00
EasyProgressMeter . EasyProgressMeterData . CurrentValue = int ( current_value )
EasyProgressMeter . EasyProgressMeterData . MaxValue = int ( max_value )
2018-07-11 19:19:24 +00:00
EasyProgressMeter . EasyProgressMeterData . ComputeProgressStats ( )
message = ' '
for line in EasyProgressMeter . EasyProgressMeterData . StatMessages :
message = message + str ( line ) + ' \n '
message = " \n " . join ( EasyProgressMeter . EasyProgressMeterData . StatMessages )
2018-07-24 18:12:39 +00:00
args = args + ( message , )
2018-08-12 21:45:12 +00:00
rc = _ProgressMeterUpdate ( EasyProgressMeter . EasyProgressMeterData . MeterID , current_value ,
EasyProgressMeter . EasyProgressMeterData . MeterText , * args )
2018-07-11 19:19:24 +00:00
# if counter >= max then the progress meter is all done. Indicate none running
2018-07-16 18:52:16 +00:00
if current_value > = EasyProgressMeter . EasyProgressMeterData . MaxValue or not rc :
2018-07-11 19:19:24 +00:00
EasyProgressMeter . EasyProgressMeterData . MeterID = None
del ( EasyProgressMeter . EasyProgressMeterData )
EasyProgressMeter . EasyProgressMeterData = EasyProgressMeterDataClass ( ) # setup a new progress meter
return False # even though at the end, return True so don't cause error with the app
return rc # return whatever the update told us
2018-07-16 18:52:16 +00:00
def EasyProgressMeterCancel ( title , * args ) :
2018-07-11 19:19:24 +00:00
EasyProgressMeter . EasyProgressMeterData = getattr ( EasyProgressMeter , ' EasyProgressMeterData ' , EasyProgressMeterDataClass ( ) )
if EasyProgressMeter . EasyProgressMeterData . MeterID is not None :
# tell the normal meter update that we're at max value which will close the meter
2018-07-16 18:52:16 +00:00
rc = EasyProgressMeter ( title , EasyProgressMeter . EasyProgressMeterData . MaxValue , EasyProgressMeter . EasyProgressMeterData . MaxValue , ' *** CANCELLING *** ' , ' Caller requested a cancel ' , * args )
2018-07-11 19:19:24 +00:00
return rc
return True
# input is #RRGGBB
# output is #RRGGBB
def GetComplimentaryHex ( color ) :
# 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
2018-07-21 00:07:04 +00:00
# ======================== EasyPrint =====#
# ===================================================#
_easy_print_data = None # global variable... I'm cheating
class DebugWin ( ) :
def __init__ ( self , size = ( None , None ) ) :
# Show a form that's a running counter
win_size = size if size != ( None , None ) else DEFAULT_DEBUG_WINDOW_SIZE
self . form = FlexForm ( ' Debug Window ' , auto_size_text = True , font = ( ' Courier New ' , 12 ) )
self . output_element = Output ( size = win_size )
self . form_rows = [ [ Text ( ' EasyPrint Output ' ) ] ,
[ self . output_element ] ,
[ Quit ( ) ] ]
self . form . AddRows ( self . form_rows )
self . form . Show ( non_blocking = True ) # Show a ;non-blocking form, returns immediately
return
def Print ( self , * args , end = None , sep = None ) :
sepchar = sep if sep is not None else ' '
endchar = end if end is not None else ' \n '
print ( * args , sep = sepchar , end = endchar )
# for a in args:
# msg = str(a)
# print(msg, end="", sep=sepchar)
# print(1, 2, 3, sep='-')
# if end is None:
# print("")
2018-08-14 20:49:36 +00:00
self . form . ReadNonBlocking ( )
2018-07-21 00:07:04 +00:00
def Close ( self ) :
self . form . CloseNonBlockingForm ( )
self . form . __del__ ( )
def Print ( * args , size = ( None , None ) , end = None , sep = None ) :
EasyPrint ( * args , size = size , end = end , sep = sep )
def PrintClose ( ) :
EasyPrintClose ( )
def eprint ( * args , size = ( None , None ) , end = None , sep = None ) :
EasyPrint ( * args , size = size , end = end , sep = sep )
def EasyPrint ( * args , size = ( None , None ) , end = None , sep = None ) :
2018-08-06 16:20:18 +00:00
global _easy_print_data
if _easy_print_data is None :
_easy_print_data = DebugWin ( size = size )
_easy_print_data . Print ( * args , end = end , sep = sep )
def EasyPrintold ( * args , size = ( None , None ) , end = None , sep = None ) :
2018-07-21 00:07:04 +00:00
if ' easy_print_data ' not in EasyPrint . __dict__ : # use a function property to save DebugWin object (static variable)
EasyPrint . easy_print_data = DebugWin ( size = size )
if EasyPrint . easy_print_data is None :
EasyPrint . easy_print_data = DebugWin ( size = size )
EasyPrint . easy_print_data . Print ( * args , end = end , sep = sep )
def EasyPrintClose ( ) :
if ' easy_print_data ' in EasyPrint . __dict__ :
if EasyPrint . easy_print_data is not None :
2018-08-06 16:20:18 +00:00
EasyPrint . easy_print_data . _Close ( )
2018-07-21 00:07:04 +00:00
EasyPrint . easy_print_data = None
# del EasyPrint.easy_print_data
2018-07-11 19:19:24 +00:00
# ======================== Scrolled Text Box =====#
# ===================================================#
2018-07-16 18:52:16 +00:00
def ScrolledTextBox ( * args , button_color = None , yes_no = False , auto_close = False , auto_close_duration = None , height = None ) :
2018-07-11 19:19:24 +00:00
if not args : return
2018-07-16 18:52:16 +00:00
with FlexForm ( args [ 0 ] , auto_size_text = True , button_color = button_color , auto_close = auto_close , auto_close_duration = auto_close_duration ) as form :
max_line_total , max_line_width , total_lines , height_computed = 0 , 0 , 0 , 0
2018-07-11 19:19:24 +00:00
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 ( l ) for l in message . split ( ' \n ' ) ] )
width_used = min ( longest_line_len , MESSAGE_BOX_LINE_WIDTH )
max_line_total = max ( max_line_total , width_used )
max_line_width = MESSAGE_BOX_LINE_WIDTH
lines_needed = _GetNumLinesNeeded ( message , width_used )
2018-07-16 18:52:16 +00:00
height_computed + = lines_needed
2018-07-11 19:19:24 +00:00
complete_output + = message + ' \n '
total_lines + = lines_needed
2018-07-16 18:52:16 +00:00
height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed
if height :
height_computed = height
2018-08-09 13:35:21 +00:00
form . AddRow ( Multiline ( complete_output , size = ( max_line_width , height_computed ) ) )
2018-07-11 19:19:24 +00:00
pad = max_line_total - 15 if max_line_total > 15 else 1
# show either an OK or Yes/No depending on paramater
2018-07-16 18:52:16 +00:00
if yes_no :
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , Yes ( ) , No ( ) )
2018-07-11 19:19:24 +00:00
( button_text , values ) = form . Show ( )
return button_text == ' Yes '
else :
2018-07-16 18:52:16 +00:00
form . AddRow ( Text ( ' ' , size = ( pad , 1 ) , auto_size_text = False ) , SimpleButton ( ' OK ' , size = ( 5 , 1 ) , button_color = button_color ) )
2018-07-11 19:19:24 +00:00
form . Show ( )
# ---------------------------------------------------------------------- #
# GetPathBox #
# Pre-made dialog that looks like this roughly #
# MESSAGE #
# __________________________ #
# |__________________________| (BROWSE) #
# (SUBMIT) (CANCEL) #
# RETURNS two values: #
# True/False, path #
# (True if Submit was pressed, false otherwise) #
# ---------------------------------------------------------------------- #
2018-07-16 18:52:16 +00:00
def GetPathBox ( title , message , default_path = ' ' , button_color = None , size = ( None , None ) ) :
with FlexForm ( title , auto_size_text = True , button_color = button_color ) as form :
layout = [ [ Text ( message , auto_size_text = True ) ] ,
[ InputText ( default_text = default_path , size = size ) , FolderBrowse ( ) ] ,
2018-07-11 19:19:24 +00:00
[ Submit ( ) , Cancel ( ) ] ]
( button , input_values ) = form . LayoutAndShow ( layout )
if button != ' Submit ' :
return False , None
else :
path = input_values [ 0 ]
return True , path
# ============================== GetFileBox =========#
# Like the Get folder box but for files #
# ===================================================#
2018-07-16 18:52:16 +00:00
def GetFileBox ( title , message , default_path = ' ' , file_types = ( ( " ALL Files " , " *.* " ) , ) , button_color = None , size = ( None , None ) ) :
with FlexForm ( title , auto_size_text = True , button_color = button_color ) as form :
layout = [ [ Text ( message , auto_size_text = True ) ] ,
[ InputText ( default_text = default_path , size = size ) , FileBrowse ( file_types = file_types ) ] ,
2018-07-11 19:19:24 +00:00
[ Submit ( ) , Cancel ( ) ] ]
( button , input_values ) = form . LayoutAndShow ( layout )
if button != ' Submit ' :
return False , None
else :
path = input_values [ 0 ]
return True , path
# ============================== GetTextBox =========#
# Get a single line of text #
# ===================================================#
2018-07-16 18:52:16 +00:00
def GetTextBox ( title , message , Default = ' ' , button_color = None , size = ( None , None ) ) :
with FlexForm ( title , auto_size_text = True , button_color = button_color ) as form :
layout = [ [ Text ( message , auto_size_text = True ) ] ,
[ InputText ( default_text = Default , size = size ) ] ,
2018-07-11 19:19:24 +00:00
[ Submit ( ) , Cancel ( ) ] ]
( button , input_values ) = form . LayoutAndShow ( layout )
if button != ' Submit ' :
return False , None
else :
return True , input_values [ 0 ]
# ============================== SetGlobalIcon ======#
# Sets the icon to be used by default #
# ===================================================#
2018-07-16 18:52:16 +00:00
def SetGlobalIcon ( icon ) :
2018-07-11 19:19:24 +00:00
global _my_windows
try :
2018-07-16 18:52:16 +00:00
with open ( icon , ' r ' ) as icon_file :
2018-07-11 19:19:24 +00:00
pass
except :
raise FileNotFoundError
2018-07-16 18:52:16 +00:00
_my_windows . user_defined_icon = icon
2018-07-11 19:19:24 +00:00
return True
2018-07-18 02:08:11 +00:00
# ============================== SetOptions =========#
# Sets the icon to be used by default #
# ===================================================#
2018-08-28 16:25:15 +00:00
def SetOptions ( icon = None , button_color = None , element_size = ( None , None ) , button_element_size = ( None , None ) , margins = ( None , None ) ,
2018-07-27 19:45:38 +00:00
element_padding = ( None , None ) , auto_size_text = None , auto_size_buttons = None , font = None , border_width = None ,
2018-07-23 19:14:34 +00:00
slider_border_width = None , slider_relief = None , slider_orientation = None ,
autoclose_time = None , message_box_line_width = None ,
2018-07-26 23:53:17 +00:00
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 ,
2018-08-16 13:21:30 +00:00
text_element_background_color = None , input_elements_background_color = None , input_text_color = None ,
2018-08-07 11:30:51 +00:00
scrollbar_color = None , text_color = None , element_text_color = None , debug_win_size = ( None , None ) , window_location = ( None , None ) ) :
2018-07-23 19:14:34 +00:00
2018-07-18 02:08:11 +00:00
global DEFAULT_ELEMENT_SIZE
2018-08-28 16:25:15 +00:00
global DEFAULT_BUTTON_ELEMENT_SIZE
2018-07-18 02:08:11 +00:00
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
2018-07-27 19:45:38 +00:00
global DEFAULT_AUTOSIZE_BUTTONS
2018-07-18 02:08:11 +00:00
global DEFAULT_FONT
global DEFAULT_BORDER_WIDTH
global DEFAULT_AUTOCLOSE_TIME
global DEFAULT_BUTTON_COLOR
2018-07-18 19:11:22 +00:00
global MESSAGE_BOX_LINE_WIDTH
global DEFAULT_PROGRESS_BAR_BORDER_WIDTH
2018-07-26 23:53:17 +00:00
global DEFAULT_PROGRESS_BAR_STYLE
global DEFAULT_PROGRESS_BAR_RELIEF
global DEFAULT_PROGRESS_BAR_COLOR
global DEFAULT_PROGRESS_BAR_SIZE
2018-07-21 00:07:04 +00:00
global DEFAULT_TEXT_JUSTIFICATION
global DEFAULT_DEBUG_WINDOW_SIZE
2018-07-23 19:14:34 +00:00
global DEFAULT_SLIDER_BORDER_WIDTH
global DEFAULT_SLIDER_RELIEF
global DEFAULT_SLIDER_ORIENTATION
2018-07-26 23:53:17 +00:00
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
2018-07-30 21:00:42 +00:00
global DEFAULT_WINDOW_LOCATION
2018-08-07 11:30:51 +00:00
global DEFAULT_ELEMENT_TEXT_COLOR
2018-08-16 13:21:30 +00:00
global DEFAULT_INPUT_TEXT_COLOR
2018-07-18 02:08:11 +00:00
global _my_windows
if icon :
try :
with open ( icon , ' r ' ) as icon_file :
pass
except :
raise FileNotFoundError
_my_windows . user_defined_icon = icon
2018-08-06 16:20:18 +00:00
if button_color != None :
DEFAULT_BUTTON_COLOR = button_color
2018-07-18 02:08:11 +00:00
2018-07-18 19:11:22 +00:00
if element_size != ( None , None ) :
DEFAULT_ELEMENT_SIZE = element_size
2018-07-18 02:08:11 +00:00
2018-08-28 16:25:15 +00:00
if button_element_size != ( None , None ) :
DEFAULT_BUTTON_ELEMENT_SIZE = button_element_size
2018-07-18 19:11:22 +00:00
if margins != ( None , None ) :
DEFAULT_MARGINS = margins
2018-07-18 02:08:11 +00:00
2018-07-18 19:11:22 +00:00
if element_padding != ( None , None ) :
DEFAULT_ELEMENT_PADDING = element_padding
2018-07-18 02:08:11 +00:00
2018-07-27 19:45:38 +00:00
if auto_size_text != None :
2018-07-18 19:11:22 +00:00
DEFAULT_AUTOSIZE_TEXT = auto_size_text
2018-07-18 02:08:11 +00:00
2018-07-27 19:45:38 +00:00
if auto_size_buttons != None :
DEFAULT_AUTOSIZE_BUTTONS = auto_size_buttons
2018-07-18 19:11:22 +00:00
if font != None :
DEFAULT_FONT = font
2018-07-18 02:08:11 +00:00
2018-07-18 19:11:22 +00:00
if border_width != None :
DEFAULT_BORDER_WIDTH = border_width
2018-07-18 02:08:11 +00:00
2018-07-18 19:11:22 +00:00
if autoclose_time != None :
DEFAULT_AUTOCLOSE_TIME = autoclose_time
2018-07-18 02:08:11 +00:00
2018-07-18 19:11:22 +00:00
if message_box_line_width != None :
MESSAGE_BOX_LINE_WIDTH = message_box_line_width
2018-07-18 02:08:11 +00:00
2018-07-18 19:11:22 +00:00
if progress_meter_border_depth != None :
DEFAULT_PROGRESS_BAR_BORDER_WIDTH = progress_meter_border_depth
2018-07-18 02:08:11 +00:00
2018-07-26 23:53:17 +00:00
if progress_meter_style != None :
DEFAULT_PROGRESS_BAR_STYLE = progress_meter_style
if progress_meter_relief != None :
DEFAULT_PROGRESS_BAR_RELIEF = progress_meter_relief
if progress_meter_color != None :
DEFAULT_PROGRESS_BAR_COLOR = progress_meter_color
if progress_meter_size != None :
DEFAULT_PROGRESS_BAR_SIZE = progress_meter_size
2018-07-23 19:14:34 +00:00
if slider_border_width != None :
DEFAULT_SLIDER_BORDER_WIDTH = slider_border_width
if slider_orientation != None :
DEFAULT_SLIDER_ORIENTATION = slider_orientation
if slider_relief != None :
DEFAULT_SLIDER_RELIEF = slider_relief
2018-07-21 00:07:04 +00:00
if text_justification != None :
DEFAULT_TEXT_JUSTIFICATION = text_justification
2018-07-18 02:08:11 +00:00
2018-07-26 23:53:17 +00:00
if background_color != None :
DEFAULT_BACKGROUND_COLOR = background_color
if text_element_background_color != None :
DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR = text_element_background_color
if input_elements_background_color != None :
DEFAULT_INPUT_ELEMENTS_COLOR = input_elements_background_color
if element_background_color != None :
DEFAULT_ELEMENT_BACKGROUND_COLOR = element_background_color
2018-07-30 21:00:42 +00:00
if window_location != ( None , None ) :
DEFAULT_WINDOW_LOCATION = window_location
2018-07-21 00:07:04 +00:00
if debug_win_size != ( None , None ) :
DEFAULT_DEBUG_WINDOW_SIZE = debug_win_size
2018-07-26 23:53:17 +00:00
if text_color != None :
DEFAULT_TEXT_COLOR = text_color
if scrollbar_color != None :
DEFAULT_SCROLLBAR_COLOR = scrollbar_color
2018-08-07 11:30:51 +00:00
if element_text_color != None :
DEFAULT_ELEMENT_TEXT_COLOR = element_text_color
2018-08-16 13:21:30 +00:00
if input_text_color is not None :
DEFAULT_INPUT_TEXT_COLOR = input_text_color
2018-07-21 00:07:04 +00:00
return True
2018-07-18 02:08:11 +00:00
2018-08-12 21:45:12 +00:00
#################### ChangeLookAndFeel #######################
# Predefined settings that will change the colors and styles #
# of the elements. #
##############################################################
def ChangeLookAndFeel ( index ) :
# look and feel table
2018-08-28 03:57:52 +00:00
look_and_feel = { ' GreenTan ' : { ' BACKGROUND ' : ' #9FB8AD ' , ' TEXT ' : COLOR_SYSTEM_DEFAULT , ' INPUT ' : ' #F7F3EC ' , ' TEXT_INPUT ' : ' black ' , ' SCROLL ' : ' #F7F3EC ' , ' BUTTON ' : ( ' white ' , ' #475841 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
2018-08-16 13:21:30 +00:00
2018-08-28 03:57:52 +00:00
' LightGreen ' : { ' BACKGROUND ' : ' #B7CECE ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #FDFFF7 ' , ' TEXT_INPUT ' : ' black ' , ' SCROLL ' : ' #FDFFF7 ' , ' BUTTON ' : ( ' white ' , ' #658268 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
2018-08-16 13:21:30 +00:00
' BluePurple ' : { ' BACKGROUND ' : ' #A5CADD ' , ' TEXT ' : ' #6E266E ' , ' INPUT ' : ' #E0F5FF ' , ' TEXT_INPUT ' : ' black ' , ' SCROLL ' : ' #E0F5FF ' , ' BUTTON ' : ( ' white ' , ' #303952 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
' Purple ' : { ' BACKGROUND ' : ' #B0AAC2 ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #F2EFE8 ' , ' SCROLL ' : ' #F2EFE8 ' , ' TEXT_INPUT ' : ' black ' ,
' BUTTON ' : ( ' black ' , ' #C2D4D8 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
' BlueMono ' : { ' BACKGROUND ' : ' #AAB6D3 ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #F1F4FC ' , ' SCROLL ' : ' #F1F4FC ' , ' TEXT_INPUT ' : ' black ' ,
' BUTTON ' : ( ' white ' , ' #7186C7 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
' GreenMono ' : { ' BACKGROUND ' : ' #A8C1B4 ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #DDE0DE ' , ' SCROLL ' : ' #E3E3E3 ' , ' TEXT_INPUT ' : ' black ' ,
' BUTTON ' : ( ' white ' , ' #6D9F85 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
2018-08-18 20:52:24 +00:00
' BrownBlue ' : { ' BACKGROUND ' : ' #64778d ' , ' TEXT ' : ' white ' , ' INPUT ' : ' #f0f3f7 ' , ' SCROLL ' : ' #A6B2BE ' , ' TEXT_INPUT ' : ' black ' , ' BUTTON ' : ( ' white ' , ' #283b5b ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
' BrightColors ' : { ' BACKGROUND ' : ' #b4ffb4 ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #ffff64 ' , ' SCROLL ' : ' #ffb482 ' , ' TEXT_INPUT ' : ' black ' , ' BUTTON ' : ( ' black ' , ' #ffa0dc ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
' NeutralBlue ' : { ' BACKGROUND ' : ' #92aa9d ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #fcfff6 ' ,
' SCROLL ' : ' #fcfff6 ' , ' TEXT_INPUT ' : ' black ' , ' BUTTON ' : ( ' black ' , ' #d0dbbd ' ) ,
' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
' Kayak ' : { ' BACKGROUND ' : ' #a7ad7f ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #e6d3a8 ' ,
' SCROLL ' : ' #e6d3a8 ' , ' TEXT_INPUT ' : ' black ' , ' BUTTON ' : ( ' white ' , ' #5d907d ' ) ,
' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
2018-08-12 21:45:12 +00:00
2018-08-18 20:52:24 +00:00
' SandyBeach ' : { ' BACKGROUND ' : ' #efeccb ' , ' TEXT ' : ' #012f2f ' , ' INPUT ' : ' #e6d3a8 ' ,
' SCROLL ' : ' #e6d3a8 ' , ' TEXT_INPUT ' : ' #012f2f ' , ' BUTTON ' : ( ' white ' , ' #046380 ' ) ,
' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR } ,
2018-08-12 21:45:12 +00:00
2018-08-18 20:52:24 +00:00
' TealMono ' : { ' BACKGROUND ' : ' #a8cfdd ' , ' TEXT ' : ' black ' , ' INPUT ' : ' #dfedf2 ' , ' SCROLL ' : ' #dfedf2 ' , ' TEXT_INPUT ' : ' black ' , ' BUTTON ' : ( ' white ' , ' #183440 ' ) , ' PROGRESS ' : DEFAULT_PROGRESS_BAR_COLOR }
2018-08-16 13:21:30 +00:00
}
2018-08-12 21:45:12 +00:00
try :
colors = look_and_feel [ index ]
SetOptions ( 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 ' ] ,
progress_meter_color = colors [ ' PROGRESS ' ] ,
border_width = 0 ,
slider_border_width = 0 ,
progress_meter_border_depth = 0 ,
2018-08-16 13:21:30 +00:00
scrollbar_color = ( colors [ ' SCROLL ' ] ) ,
element_text_color = colors [ ' TEXT ' ] ,
input_text_color = colors [ ' TEXT_INPUT ' ] )
2018-08-12 21:45:12 +00:00
except : # most likely an index out of range
pass
2018-07-26 23:53:17 +00:00
# ============================== sprint ======#
2018-07-11 19:19:24 +00:00
# Is identical to the Scrolled Text Box #
# Provides a crude 'print' mechanism but in a #
# GUI environment #
# ============================================#
sprint = ScrolledTextBox
2018-07-23 19:14:34 +00:00
# Converts an object's contents into a nice printable string. Great for dumping debug data
2018-08-20 17:48:02 +00:00
def ObjToStringSingleObj ( obj ) :
if obj is None :
return ' None '
2018-07-23 19:14:34 +00:00
return str ( obj . __class__ ) + ' \n ' + ' \n ' . join (
( repr ( item ) + ' = ' + repr ( obj . __dict__ [ item ] ) for item in sorted ( obj . __dict__ ) ) )
def ObjToString ( obj , extra = ' ' ) :
2018-08-20 17:48:02 +00:00
if obj is None :
return ' None '
2018-07-23 19:14:34 +00:00
return str ( obj . __class__ ) + ' \n ' + ' \n ' . join (
( extra + ( str ( item ) + ' = ' +
( ObjToString ( obj . __dict__ [ item ] , extra + ' ' ) if hasattr ( obj . __dict__ [ item ] , ' __dict__ ' ) else str (
obj . __dict__ [ item ] ) ) )
2018-07-27 19:45:38 +00:00
for item in sorted ( obj . __dict__ ) ) )
def main ( ) :
2018-08-12 21:45:12 +00:00
with FlexForm ( ' Demo form.. ' ) as form :
2018-07-27 19:45:38 +00:00
form_rows = [ [ Text ( ' You are running the PySimpleGUI.py file itself ' ) ] ,
2018-08-22 19:48:28 +00:00
[ Text ( ' You should be importing it rather than running it ' , size = ( 50 , 2 ) ) ] ,
2018-07-27 19:45:38 +00:00
[ Text ( ' Here is your sample input form.... ' ) ] ,
2018-08-12 21:45:12 +00:00
[ Text ( ' Source Folder ' , size = ( 15 , 1 ) , justification = ' right ' ) , InputText ( ' Source ' , focus = True ) , FolderBrowse ( ) ] ,
[ Text ( ' Destination Folder ' , size = ( 15 , 1 ) , justification = ' right ' ) , InputText ( ' Dest ' ) , FolderBrowse ( ) ] ,
2018-08-09 13:35:21 +00:00
[ Submit ( bind_return_key = True ) , Cancel ( ) ] ]
2018-07-27 19:45:38 +00:00
button , ( source , dest ) = form . LayoutAndRead ( form_rows )
if __name__ == ' __main__ ' :
main ( )
2018-08-12 02:31:42 +00:00
exit ( 69 )