PySimpleGUI/PySimpleGUI27Gen.py

5099 lines
247 KiB
Python

#!/usr/bin/python3
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals
from __future__ import absolute_import
from builtins import super
from builtins import open
from builtins import range
from builtins import int
from builtins import str
from builtins import object
from future import standard_library
standard_library.install_aliases()
import sys
if sys.version_info[0] >= 3:
import tkinter as tk
from tkinter import filedialog
from tkinter.colorchooser import askcolor
from tkinter import ttk
import tkinter.scrolledtext as tkst
import tkinter.font
else:
import tkinter as tk
# import tkinter as tk
import tkinter.filedialog
import tkinter.ttk
import tkinter.colorchooser
import tkinter.font
import tkinter.scrolledtext
import datetime
import sys
import textwrap
import pickle
import calendar
g_time_start = 0
g_time_end = 0
g_time_delta = 0
import time
def TimerStart():
global g_time_start
g_time_start = time.time()
def TimerStop():
global g_time_delta, g_time_end
g_time_end = time.time()
g_time_delta = g_time_end - g_time_start
print(g_time_delta)
# ----====----====----==== Constants the user CAN safely change ====----====----====----#
DEFAULT_WINDOW_ICON = 'default_icon.ico'
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
DEFAULT_BUTTON_ELEMENT_SIZE = (10,1) # In CHARACTERS
DEFAULT_MARGINS = (10,5) # Margins for each LEFT/RIGHT margin is first term
DEFAULT_ELEMENT_PADDING = (5,3) # Padding between elements (row, col) in pixels
DEFAULT_AUTOSIZE_TEXT = True
DEFAULT_AUTOSIZE_BUTTONS = True
DEFAULT_FONT = ("Helvetica", 10)
DEFAULT_TEXT_JUSTIFICATION = 'left'
DEFAULT_BORDER_WIDTH = 1
DEFAULT_AUTOCLOSE_TIME = 3 # time in seconds to show an autoclose form
DEFAULT_DEBUG_WINDOW_SIZE = (80,20)
DEFAULT_WINDOW_LOCATION = (None,None)
MAX_SCROLLED_TEXT_BOX_HEIGHT = 50
DEFAULT_TOOLTIP_TIME = 400
#################### COLOR STUFF ####################
BLUES = ("#082567","#0A37A3","#00345B")
PURPLES = ("#480656","#4F2398","#380474")
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
YELLOWS = ("#F3FB62", "#F0F595")
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]), ('#000000','#FFFFFF'),('#FFFFFF', '#000000'), (YELLOWS[0], PURPLES[1]),
(YELLOWS[0], GREENS[3]), (YELLOWS[0], BLUES[2]))
COLOR_SYSTEM_DEFAULT = '1234567890' # Colors should never be this long
if sys.platform == 'darwin':
DEFAULT_BUTTON_COLOR = COLOR_SYSTEM_DEFAULT # Foreground, Background (None, None) == System Default
OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR = COLOR_SYSTEM_DEFAULT # Colors should never be this long
else:
DEFAULT_BUTTON_COLOR = ('white', BLUES[0]) # Foreground, Background (None, None) == System Default
OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR = ('white', BLUES[0]) # Colors should never be this long
DEFAULT_ERROR_BUTTON_COLOR =("#FFFFFF", "#FF0000")
DEFAULT_BACKGROUND_COLOR = None
DEFAULT_ELEMENT_BACKGROUND_COLOR = None
DEFAULT_ELEMENT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR = None
DEFAULT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_INPUT_ELEMENTS_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_INPUT_TEXT_COLOR = COLOR_SYSTEM_DEFAULT
DEFAULT_SCROLLBAR_COLOR = None
# 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
# DEFAULT_BUTTON_COLOR = (YELLOWS[0], GREENS[4]) # Foreground, Background (None, None) == System Default
# DEFAULT_BUTTON_COLOR = ('white', 'black') # Foreground, Background (None, None) == System Default
# 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
# A transparent button is simply one that matches the background
TRANSPARENT_BUTTON = ('#F0F0F0', '#F0F0F0')
#--------------------------------------------------------------------------------
# Progress Bar Relief Choices
RELIEF_RAISED = 'raised'
RELIEF_SUNKEN = 'sunken'
RELIEF_FLAT = 'flat'
RELIEF_RIDGE = 'ridge'
RELIEF_GROOVE = 'groove'
RELIEF_SOLID = 'solid'
DEFAULT_PROGRESS_BAR_COLOR = (GREENS[0], '#D0D0D0') # a nice green progress bar
DEFAULT_PROGRESS_BAR_SIZE = (25,20) # Size of Progress Bar (characters for length, pixels for width)
DEFAULT_PROGRESS_BAR_BORDER_WIDTH=1
DEFAULT_PROGRESS_BAR_RELIEF = RELIEF_GROOVE
PROGRESS_BAR_STYLES = ('default','winnative', 'clam', 'alt', 'classic', 'vista', 'xpnative')
DEFAULT_PROGRESS_BAR_STYLE = 'default'
DEFAULT_METER_ORIENTATION = 'Horizontal'
DEFAULT_SLIDER_ORIENTATION = 'vertical'
DEFAULT_SLIDER_BORDER_WIDTH=1
DEFAULT_SLIDER_RELIEF = tk.FLAT
DEFAULT_FRAME_RELIEF = tk.GROOVE
DEFAULT_LISTBOX_SELECT_MODE = tk.SINGLE
SELECT_MODE_MULTIPLE = tk.MULTIPLE
LISTBOX_SELECT_MODE_MULTIPLE = 'multiple'
SELECT_MODE_BROWSE = tk.BROWSE
LISTBOX_SELECT_MODE_BROWSE = 'browse'
SELECT_MODE_EXTENDED = tk.EXTENDED
LISTBOX_SELECT_MODE_EXTENDED = 'extended'
SELECT_MODE_SINGLE = tk.SINGLE
LISTBOX_SELECT_MODE_SINGLE = 'single'
TABLE_SELECT_MODE_NONE = tk.NONE
TABLE_SELECT_MODE_BROWSE = tk.BROWSE
TABLE_SELECT_MODE_EXTENDED = tk.EXTENDED
DEFAULT_TABLE_SECECT_MODE = TABLE_SELECT_MODE_EXTENDED
TITLE_LOCATION_TOP = tk.N
TITLE_LOCATION_BOTTOM = tk.S
TITLE_LOCATION_LEFT = tk.W
TITLE_LOCATION_RIGHT = tk.E
TITLE_LOCATION_TOP_LEFT = tk.NW
TITLE_LOCATION_TOP_RIGHT = tk.NE
TITLE_LOCATION_BOTTOM_LEFT = tk.SW
TITLE_LOCATION_BOTTOM_RIGHT = tk.SE
# DEFAULT_METER_ORIENTATION = 'Vertical'
# ----====----====----==== Constants the user should NOT f-with ====----====----====----#
ThisRow = 555666777 # magic number
# 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(object):
def __init__(self):
self.NumOpenWindows = 0
self.user_defined_icon = None
def Decrement(self):
self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0
# print('---- DECREMENTING Num Open Windows = {} ---'.format(self.NumOpenWindows))
def Increment(self):
self.NumOpenWindows += 1
# print('++++ INCREMENTING Num Open Windows = {} ++++'.format(self.NumOpenWindows))
_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):
BUTTON_TYPE_BROWSE_FOLDER = 1
BUTTON_TYPE_BROWSE_FILE = 2
BUTTON_TYPE_BROWSE_FILES = 21
BUTTON_TYPE_SAVEAS_FILE = 3
BUTTON_TYPE_CLOSES_WIN = 5
BUTTON_TYPE_CLOSES_WIN_ONLY = 6
BUTTON_TYPE_READ_FORM = 7
BUTTON_TYPE_REALTIME = 9
BUTTON_TYPE_CALENDAR_CHOOSER = 30
BUTTON_TYPE_COLOR_CHOOSER = 40
# ------------------------- Element types ------------------------- #
# class ElementType(Enum):
ELEM_TYPE_TEXT = 1
ELEM_TYPE_INPUT_TEXT = 20
ELEM_TYPE_INPUT_COMBO = 21
ELEM_TYPE_INPUT_OPTION_MENU = 22
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
ELEM_TYPE_CANVAS = 40
ELEM_TYPE_FRAME = 41
ELEM_TYPE_GRAPH = 42
ELEM_TYPE_TAB = 50
ELEM_TYPE_TAB_GROUP = 51
ELEM_TYPE_INPUT_SLIDER = 10
ELEM_TYPE_INPUT_LISTBOX = 11
ELEM_TYPE_OUTPUT = 300
ELEM_TYPE_COLUMN = 555
ELEM_TYPE_MENUBAR = 600
ELEM_TYPE_PROGRESS_BAR = 200
ELEM_TYPE_BLANK = 100
ELEM_TYPE_TABLE = 700
ELEM_TYPE_ERROR = 666
# ------------------------- Popup Buttons Types ------------------------- #
POPUP_BUTTONS_YES_NO = 1
POPUP_BUTTONS_CANCELLED = 2
POPUP_BUTTONS_ERROR = 3
POPUP_BUTTONS_OK_CANCEL = 4
POPUP_BUTTONS_OK = 0
POPUP_BUTTONS_NO_BUTTONS = 5
# ------------------------------------------------------------------------- #
# ToolTip used by the Elements #
# ------------------------------------------------------------------------- #
class ToolTip(object):
""" Create a tooltip for a given widget
(inspired by https://stackoverflow.com/a/36221216)
"""
def __init__(self, widget, text, timeout=DEFAULT_TOOLTIP_TIME):
self.widget = widget
self.text = text
self.timeout = timeout
#self.wraplength = wraplength if wraplength else widget.winfo_screenwidth() // 2
self.tipwindow = None
self.id = None
self.x = self.y = 0
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.leave)
self.widget.bind("<ButtonPress>", self.leave)
def enter(self, event=None):
self.schedule()
def leave(self, event=None):
self.unschedule()
self.hidetip()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.timeout, self.showtip)
def unschedule(self):
if self.id:
self.widget.after_cancel(self.id)
self.id = None
def showtip(self):
if self.tipwindow:
return
x = self.widget.winfo_rootx() + 20
y = self.widget.winfo_rooty() + self.widget.winfo_height() - 20
self.tipwindow = tk.Toplevel(self.widget)
self.tipwindow.wm_overrideredirect(True)
self.tipwindow.wm_geometry("+%d+%d" % (x, y))
label = tkinter.ttk.Label(self.tipwindow, text=self.text, justify=tk.LEFT,
background="#ffffe0", relief=tk.SOLID, borderwidth=1)
label.pack()
def hidetip(self):
if self.tipwindow:
self.tipwindow.destroy()
self.tipwindow = None
# ---------------------------------------------------------------------- #
# Cascading structure.... Objects get larger #
# Button #
# Element #
# Row #
# Form #
# ---------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
# Element CLASS #
# ------------------------------------------------------------------------- #
class Element(object):
def __init__(self, type, size=(None, None), auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
self.Size = size
self.Type = type
self.AutoSizeText = auto_size_text
self.Pad = DEFAULT_ELEMENT_PADDING if pad is None else pad
self.Font = font
self.TKStringVar = None
self.TKIntVar = None
self.TKText = None
self.TKEntry = None
self.TKImage = None
self.ParentForm=None
self.ParentContainer=None # will be a Form, Column, or Frame element
self.TextInputDefault = None
self.Position = (0,0) # Default position Row 0, Col 0
self.BackgroundColor = background_color if background_color is not None else DEFAULT_ELEMENT_BACKGROUND_COLOR
self.TextColor = text_color if text_color is not None else DEFAULT_ELEMENT_TEXT_COLOR
self.Key = key # dictionary key for return values
self.Tooltip = tooltip
self.TooltipObject = None
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
if element.Type == ELEM_TYPE_FRAME:
rc = self.FindReturnKeyBoundButton(element)
if rc is not None:
return rc
if element.Type == ELEM_TYPE_TAB_GROUP:
rc = self.FindReturnKeyBoundButton(element)
if rc is not None:
return rc
if element.Type == ELEM_TYPE_TAB:
rc = self.FindReturnKeyBoundButton(element)
if rc is not None:
return rc
return None
def TextClickedHandler(self, event):
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = self.DisplayText
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def ReturnKeyHandler(self, event):
MyForm = self.ParentForm
button_element = self.FindReturnKeyBoundButton(MyForm)
if button_element is not None:
button_element.ButtonCallBack()
def ListboxSelectHandler(self, event):
MyForm = self.ParentForm
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def ComboboxSelectHandler(self, event):
MyForm = self.ParentForm
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def CheckboxHandler(self):
MyForm = self.ParentForm
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit()
def TabGroupSelectHandler(self, event):
MyForm = self.ParentForm
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit()
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
# ---------------------------------------------------------------------- #
# Input Class #
# ---------------------------------------------------------------------- #
class InputText(Element):
def __init__(self, default_text ='', size=(None, None), auto_size_text=None, password_char='',
justification=None, background_color=None, text_color=None, font=None, tooltip=None, do_not_clear=False, key=None, focus=False, pad=None):
'''
Input a line of text Element
:param default_text: Default value to display
: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
'''
self.DefaultText = default_text
self.PasswordCharacter = password_char
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
self.Focus = focus
self.do_not_clear = do_not_clear
self.Justification = justification
super().__init__(ELEM_TYPE_INPUT_TEXT, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad, font=font, tooltip=tooltip)
def Update(self, value=None, disabled=None):
if disabled is True:
self.TKEntry['state'] = 'disabled'
elif disabled is False:
self.TKEntry['state'] = 'normal'
if value is not None:
try:
self.TKStringVar.set(value)
except: pass
self.DefaultText = value
def Get(self):
return self.TKStringVar.get()
def __del__(self):
super().__del__()
# ------------------------- INPUT TEXT Element lazy functions ------------------------- #
In = InputText
Input = InputText
# ---------------------------------------------------------------------- #
# Combo #
# ---------------------------------------------------------------------- #
class InputCombo(Element):
def __init__(self, values, default_value=None, size=(None, None), auto_size_text=None, background_color=None, text_color=None, change_submits=False, disabled=False, key=None, pad=None, tooltip=None):
'''
Input Combo Box Element (also called Dropdown box)
:param values:
: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
'''
self.Values = values
self.DefaultValue = default_value
self.ChangeSubmits = change_submits
self.TKCombo = None
self.InitializeAsDisabled = disabled
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
super().__init__(ELEM_TYPE_INPUT_COMBO, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip)
def Update(self, value=None, values=None, set_to_index=None, disabled=None):
if values is not None:
try:
self.TKCombo['values'] = values
self.TKCombo.current(0)
except: pass
self.Values = values
if value is not None:
for index, v in enumerate(self.Values):
if v == value:
try:
self.TKCombo.current(index)
except: pass
self.DefaultValue = value
break
if set_to_index is not None:
try:
self.TKCombo.current(set_to_index)
self.DefaultValue = self.Values[set_to_index]
except:
pass
if disabled == True:
self.TKCombo['state'] = 'disable'
elif disabled == False:
self.TKCombo['state'] = 'enable'
def __del__(self):
try:
self.TKCombo.__del__()
except:
pass
super().__del__()
# ------------------------- INPUT COMBO Element lazy functions ------------------------- #
Combo = InputCombo
DropDown = InputCombo
Drop = InputCombo
# ---------------------------------------------------------------------- #
# Option Menu #
# ---------------------------------------------------------------------- #
class InputOptionMenu(Element):
def __init__(self, values, default_value=None, size=(None, None), auto_size_text=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
'''
Input Combo Box Element (also called Dropdown box)
:param values:
: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
'''
self.Values = values
self.DefaultValue = default_value
self.TKOptionMenu = None
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
super().__init__(ELEM_TYPE_INPUT_OPTION_MENU, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip)
def Update(self, value=None, values=None, disabled=None):
if values is not None:
self.Values = values
if self.Values is not None:
for index, v in enumerate(self.Values):
if v == value:
try:
self.TKStringVar.set(value)
except: pass
self.DefaultValue = value
break
if disabled == True:
self.TKOptionMenu['state'] = 'disabled'
elif disabled == False:
self.TKOptionMenu['state'] = 'normal'
def __del__(self):
try:
self.TKOptionMenu.__del__()
except:
pass
super().__del__()
# ------------------------- OPTION MENU Element lazy functions ------------------------- #
OptionMenu = InputOptionMenu
# ---------------------------------------------------------------------- #
# Listbox #
# ---------------------------------------------------------------------- #
class Listbox(Element):
def __init__(self, values, default_values=None, select_mode=None, change_submits=False, bind_return_key=False, size=(None, None), auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
'''
Listbox Element
:param values:
:param select_mode: SELECT_MODE_BROWSE, SELECT_MODE_EXTENDED, SELECT_MODE_MULTIPLE, SELECT_MODE_SINGLE
:param font:
: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 '''
self.Values = values
self.DefaultValues = default_values
self.TKListbox = None
self.ChangeSubmits = change_submits
self.BindReturnKey = bind_return_key
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
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
super().__init__(ELEM_TYPE_INPUT_LISTBOX, size=size, auto_size_text=auto_size_text, font=font, background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip)
def Update(self, values=None, disabled=None):
if disabled == True:
self.TKListbox.configure(state='disabled')
elif disabled == False:
self.TKListbox.configure(state='normal')
if values is not None:
self.TKListbox.delete(0, 'end')
for item in values:
self.TKListbox.insert(tk.END, item)
self.TKListbox.selection_set(0, 0)
self.Values = values
def SetValue(self, values):
for index, item in enumerate(self.Values):
try:
if item in values:
self.TKListbox.selection_set(index)
else:
self.TKListbox.selection_clear(index)
except: pass
self.DefaultValues = values
def __del__(self):
try:
self.TKListBox.__del__()
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# Radio #
# ---------------------------------------------------------------------- #
class Radio(Element):
def __init__(self, text, group_id, default=False, size=(None, None), auto_size_text=None, background_color=None, text_color=None, font=None, key=None, pad=None, tooltip=None):
'''
Radio Button Element
:param text:
:param group_id:
:param default:
: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:
'''
self.InitialState = default
self.Text = text
self.TKRadio = None
self.GroupID = group_id
self.Value = None
self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR
super().__init__(ELEM_TYPE_INPUT_RADIO, size=size, auto_size_text=auto_size_text, font=font, background_color=background_color, text_color=self.TextColor, key=key, pad=pad, tooltip=tooltip)
def Update(self, value=None, disabled=None):
location = EncodeRadioRowCol(self.Position[0], self.Position[1])
if value is not None:
try:
self.TKIntVar.set(location)
except: pass
self.InitialState = value
if disabled == True:
self.TKRadio['state'] = 'disabled'
elif disabled == False:
self.TKRadio['state'] = 'normal'
def __del__(self):
try:
self.TKRadio.__del__()
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# Checkbox #
# ---------------------------------------------------------------------- #
class Checkbox(Element):
def __init__(self, text, default=False, size=(None, None), auto_size_text=None, font=None, background_color=None, text_color=None, change_submits=False, key=None, pad=None, tooltip=None):
'''
Check Box Element
:param text:
:param default:
: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:
'''
self.Text = text
self.InitialState = default
self.Value = None
self.TKCheckbutton = None
self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR
self.ChangeSubmits = change_submits
super().__init__(ELEM_TYPE_INPUT_CHECKBOX, size=size, auto_size_text=auto_size_text, font=font,
background_color=background_color, text_color=self.TextColor, key=key, pad=pad, tooltip=tooltip)
def Get(self):
return self.TKIntVar.get()
def Update(self, value=None, disabled=None):
if value is not None:
try:
self.TKIntVar.set(value)
self.InitialState = value
except: pass
if disabled == True:
self.TKCheckbutton.configure(state='disabled')
elif disabled == False:
self.TKCheckbutton.configure(state='normal')
def __del__(self):
super().__del__()
# ------------------------- CHECKBOX Element lazy functions ------------------------- #
CB = Checkbox
CBox = Checkbox
Check = Checkbox
# ---------------------------------------------------------------------- #
# Spin #
# ---------------------------------------------------------------------- #
class Spin(Element):
# Values = None
# TKSpinBox = None
def __init__(self, values, initial_value=None, change_submits=False, size=(None, None), auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
'''
Spin Box Element
:param values:
:param initial_value:
: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:
'''
self.Values = values
self.DefaultValue = initial_value
self.ChangeSubmits = change_submits
self.TKSpinBox = None
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
super().__init__(ELEM_TYPE_INPUT_SPIN, size, auto_size_text, font=font,background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip)
return
def Update(self, value=None, values=None, disabled=None):
if values != None:
old_value = self.TKStringVar.get()
self.Values = values
self.TKSpinBox.configure(values=values)
self.TKStringVar.set(old_value)
if value is not None:
try:
self.TKStringVar.set(value)
except: pass
self.DefaultValue = value
if disabled == True:
self.TKSpinBox.configure(state='disabled')
elif disabled == False:
self.TKSpinBox.configure(state='normal')
def SpinChangedHandler(self, event):
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def __del__(self):
try:
self.TKSpinBox.__del__()
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# Multiline #
# ---------------------------------------------------------------------- #
class Multiline(Element):
def __init__(self, default_text='', enter_submits = False, autoscroll=False, size=(None, None), auto_size_text=None, background_color=None, text_color=None, do_not_clear=False, key=None, focus=False, pad=None, tooltip=None):
'''
Input Multi-line Element
:param default_text:
:param enter_submits:
: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
'''
self.DefaultText = default_text
self.EnterSubmits = enter_submits
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
self.Focus = focus
self.do_not_clear = do_not_clear
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
self.Autoscroll = autoscroll
super().__init__(ELEM_TYPE_INPUT_MULTILINE, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip)
return
def Update(self, value=None, disabled=None, append=False):
if value is not None:
try:
if not append:
self.TKText.delete('1.0', tk.END)
self.TKText.insert(tk.END, value)
except: pass
self.DefaultText = value
if self.Autoscroll:
self.TKText.see(tk.END)
if disabled == True:
self.TKText.configure(state='disabled')
elif disabled == False:
self.TKText.configure(state='normal')
def Get(self):
return self.TKText.get(1.0, tk.END)
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# Text #
# ---------------------------------------------------------------------- #
class Text(Element):
def __init__(self, text, size=(None, None), auto_size_text=None, click_submits=None, relief=None, font=None, text_color=None, background_color=None,justification=None, pad=None, key=None, tooltip=None):
'''
Text Element - Displays text in your form. Can be updated in non-blocking forms
:param text: The text to display
: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'
'''
self.DisplayText = text
self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR
self.Justification = justification
self.Relief = relief
self.ClickSubmits = click_submits
if background_color is None:
bg = DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR
else:
bg = background_color
super().__init__(ELEM_TYPE_TEXT, size, auto_size_text, background_color=bg, font=font if font else DEFAULT_FONT, text_color=self.TextColor, pad=pad, key=key, tooltip=tooltip)
return
def Update(self, value = None, background_color=None, text_color=None, font=None):
if value is not None:
self.DisplayText=value
stringvar = self.TKStringVar
stringvar.set(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)
if font is not None:
self.TKText.configure(font=font)
def __del__(self):
super().__del__()
# ------------------------- Text Element lazy functions ------------------------- #
Txt = Text
T = Text
# ---------------------------------------------------------------------- #
# TKProgressBar #
# Emulate the TK ProgressBar using canvas and rectangles
# ---------------------------------------------------------------------- #
class TKProgressBar(object):
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)):
self.Length = length
self.Width = width
self.Max = max
self.Orientation = orientation
self.Count = None
self.PriorCount = 0
if orientation[0].lower() == 'h':
s = tkinter.ttk.Style()
s.theme_use(style)
if BarColor != COLOR_SYSTEM_DEFAULT:
s.configure(str(length)+str(width)+"my.Horizontal.TProgressbar", background=BarColor[0], troughcolor=BarColor[1], troughrelief=relief, borderwidth=border_width, thickness=width)
else:
s.configure(str(length)+str(width)+"my.Horizontal.TProgressbar", troughrelief=relief, borderwidth=border_width, thickness=width)
self.TKProgressBarForReal = tkinter.ttk.Progressbar(root, maximum=self.Max, style=str(length)+str(width)+'my.Horizontal.TProgressbar', length=length, orient=tk.HORIZONTAL, mode='determinate')
else:
s = tkinter.ttk.Style()
s.theme_use(style)
if BarColor != COLOR_SYSTEM_DEFAULT:
s.configure(str(length)+str(width)+"my.Vertical.TProgressbar", background=BarColor[0], troughcolor=BarColor[1], troughrelief=relief, borderwidth=border_width, thickness=width)
else:
s.configure(str(length)+str(width)+"my.Vertical.TProgressbar", troughrelief=relief, borderwidth=border_width, thickness=width)
self.TKProgressBarForReal = tkinter.ttk.Progressbar(root, maximum=self.Max, style=str(length)+str(width)+'my.Vertical.TProgressbar', length=length, orient=tk.VERTICAL, mode='determinate')
def Update(self, count=None, max=None):
if max is not None:
self.Max = max
try:
self.TKProgressBarForReal.config(maximum=max)
except:
return False
if count is not None and count > self.Max: return False
if count is not None:
try:
self.TKProgressBarForReal['value'] = count
except: return False
return True
def __del__(self):
try:
self.TKProgressBarForReal.__del__()
except:
pass
# ---------------------------------------------------------------------- #
# 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 #
# Scroll bar will span the length of the frame #
# ---------------------------------------------------------------------- #
class TKOutput(tk.Frame):
def __init__(self, parent, width, height, bd, background_color=None, text_color=None, font=None, pad=None):
frame = tk.Frame(parent)
tk.Frame.__init__(self, frame)
self.output = tk.Text(frame, width=width, height=height, bd=bd, font=font)
if background_color and background_color != COLOR_SYSTEM_DEFAULT:
self.output.configure(background=background_color)
frame.configure(background=background_color)
if text_color and text_color != COLOR_SYSTEM_DEFAULT:
self.output.configure(fg=text_color)
self.vsb = tk.Scrollbar(frame, orient="vertical", command=self.output.yview)
self.output.configure(yscrollcommand=self.vsb.set)
self.output.pack(side="left", fill="both", expand=True)
self.vsb.pack(side="left", fill="y", expand=False)
frame.pack(side="left", padx=pad[0], pady=pad[1], expand=True, fill='y')
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
sys.stderr = self.previous_stderr
# ---------------------------------------------------------------------- #
# Output #
# Routes stdout, stderr to a scrolled window #
# ---------------------------------------------------------------------- #
class Output(Element):
def __init__(self, size=(None, None), background_color=None, text_color=None, pad=None, font=None, tooltip=None, key=None):
'''
Output Element - reroutes stdout, stderr to this window
:param size: Size of field in characters
:param background_color: Color for Element. Text or RGB Hex
'''
self._TKOut = None
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
super().__init__(ELEM_TYPE_OUTPUT, size=size, background_color=bg, text_color=fg, pad=pad, font=font, tooltip=tooltip, key=key)
@property
def TKOut(self):
if self._TKOut is None:
print('*** Did you forget to call Finalize()? Your code should look something like: ***')
print('*** form = sg.Window("My Form").Layout(layout).Finalize() ***')
return self._TKOut
def __del__(self):
try:
self._TKOut.__del__()
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# Button Class #
# ---------------------------------------------------------------------- #
class Button(Element):
def __init__(self, button_text='', button_type=BUTTON_TYPE_CLOSES_WIN, target=(None, None), tooltip=None, file_types=(("ALL Files", "*.*"),), initial_folder=None, image_filename=None, image_size=(None, None), image_subsample=None, border_width=None, size=(None, None), auto_size_button=None, button_color=None, default_value = None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
'''
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 size: Size of field in characters
:param auto_size_button:
:param button_color:
:param font:
'''
self.AutoSizeButton = auto_size_button
self.BType = button_type
self.FileTypes = file_types
self.TKButton = None
self.Target = target
self.ButtonText = button_text
self.ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR
self.ImageFilename = image_filename
self.ImageSize = image_size
self.ImageSubsample = image_subsample
self.UserData = None
self.BorderWidth = border_width if border_width is not None else DEFAULT_BORDER_WIDTH
self.BindReturnKey = bind_return_key
self.Focus = focus
self.TKCal = None
self.DefaultValue = default_value
self.InitialFolder = initial_folder
super().__init__(ELEM_TYPE_BUTTON, size=size, font=font, pad=pad, key=key, tooltip=tooltip)
return
# Realtime button release callback
def ButtonReleaseCallBack(self, parm):
self.LastButtonClickedWasRealtime = False
self.ParentForm.LastButtonClicked = None
# Realtime button callback
def ButtonPressCallBack(self, parm):
self.ParentForm.LastButtonClickedWasRealtime = True
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = self.ButtonText
# ------- Button Callback ------- #
def ButtonCallBack(self):
global _my_windows
# print(f'Parent = {self.ParentForm} Position = {self.Position}')
# 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
target_element = None
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 == (None, None):
strvar = self.TKStringVar
else:
if not isinstance(target, str):
if target[0] < 0:
target = [self.Position[0] + target[0], target[1]]
target_element = self.ParentContainer._GetElementAtLocation(target)
else:
target_element = self.ParentForm.FindElement(target)
try:
strvar = target_element.TKStringVar
except: pass
filetypes = [] if self.FileTypes is None else self.FileTypes
if self.BType == BUTTON_TYPE_BROWSE_FOLDER:
folder_name = tk.filedialog.askdirectory(initialdir=self.InitialFolder) # show the 'get folder' dialog box
if folder_name != '':
try:
strvar.set(folder_name)
self.TKStringVar.set(folder_name)
except: pass
elif self.BType == BUTTON_TYPE_BROWSE_FILE:
file_name = tk.filedialog.askopenfilename(filetypes=filetypes, initialdir=self.InitialFolder) # show the 'get file' dialog box
if file_name != '':
strvar.set(file_name)
self.TKStringVar.set(file_name)
elif self.BType == BUTTON_TYPE_COLOR_CHOOSER:
color = tk.colorchooser.askcolor() # show the 'get file' dialog box
color = color[1] # save only the #RRGGBB portion
strvar.set(color)
self.TKStringVar.set(color)
elif self.BType == BUTTON_TYPE_BROWSE_FILES:
file_name = tk.filedialog.askopenfilenames(filetypes=filetypes, initialdir=self.InitialFolder)
if file_name != '':
file_name = ';'.join(file_name)
strvar.set(file_name)
self.TKStringVar.set(file_name)
elif self.BType == BUTTON_TYPE_SAVEAS_FILE:
file_name = tk.filedialog.asksaveasfilename(filetypes=filetypes, initialdir=self.InitialFolder) # show the 'get file' dialog box
if file_name != '':
strvar.set(file_name)
self.TKStringVar.set(file_name)
elif self.BType == BUTTON_TYPE_CLOSES_WIN: # this is a return type button so GET RESULTS and destroy window
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = self.ButtonText
self.ParentForm.FormRemainedOpen = False
# if the form is tabbed, must collect all form's results and destroy all forms
self.ParentForm._Close()
self.ParentForm.TKroot.quit()
if self.ParentForm.NonBlocking:
self.ParentForm.TKroot.destroy()
_my_windows.Decrement()
elif self.BType == BUTTON_TYPE_READ_FORM: # LEAVE THE WINDOW OPEN!! DO NOT CLOSE
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = self.ButtonText
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
elif self.BType == BUTTON_TYPE_CLOSES_WIN_ONLY: # this is a return type button so GET RESULTS and destroy window
# if the form is tabbed, must collect all form's results and destroy all forms
self.ParentForm._Close()
if self.ParentForm.NonBlocking:
self.ParentForm.TKroot.destroy()
_my_windows.Decrement()
elif self.BType == BUTTON_TYPE_CALENDAR_CHOOSER: # this is a return type button so GET RESULTS and destroy window
root = tk.Toplevel()
root.title('Calendar Chooser')
self.TKCal = TKCalendar(master=root, firstweekday=calendar.SUNDAY, target_element=target_element)
self.TKCal.pack(expand=1, fill='both')
# self.ParentForm.TKRroot.mainloop()
root.update()
# root.mainloop()
# root.update()
# strvar.set(ttkcal.selection)
return
def Update(self, value=None, text=None, button_color=(None, None), disabled=None):
try:
if text is not None:
self.TKButton.configure(text=text)
self.ButtonText = text
if button_color != (None, None):
self.TKButton.config(foreground=button_color[0], background=button_color[1])
except:
return
if value is not None:
self.DefaultValue = value
if disabled == True:
self.TKButton['state'] = 'disabled'
elif disabled == False:
self.TKButton['state'] = 'normal'
def GetText(self):
return self.ButtonText
def __del__(self):
try:
self.TKButton.__del__()
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# ProgreessBar #
# ---------------------------------------------------------------------- #
class ProgressBar(Element):
def __init__(self, max_value, orientation=None, size=(None, None), auto_size_text=None, bar_color=(None, None), style=None, border_width=None, relief=None, key=None, pad=None):
'''
Progress Bar Element
:param max_value:
:param orientation:
: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:
'''
self.MaxValue = max_value
self.TKProgressBar = None
self.Cancelled = False
self.NotRunning = True
self.Orientation = orientation if orientation else DEFAULT_METER_ORIENTATION
self.BarColor = bar_color
self.BarStyle = style if style else DEFAULT_PROGRESS_BAR_STYLE
self.BorderWidth = border_width if border_width else DEFAULT_PROGRESS_BAR_BORDER_WIDTH
self.Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF
self.BarExpired = False
super().__init__(ELEM_TYPE_PROGRESS_BAR, size=size, auto_size_text=auto_size_text, key=key, pad=pad)
return
# returns False if update failed
def UpdateBar(self, current_count, max=None):
if self.ParentForm.TKrootDestroyed:
return False
self.TKProgressBar.Update(current_count, max=max)
try:
self.ParentForm.TKroot.update()
except:
_my_windows.Decrement()
return False
return True
def __del__(self):
try:
self.TKProgressBar.__del__()
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# Image #
# ---------------------------------------------------------------------- #
class Image(Element):
def __init__(self, filename=None, data=None, size=(None, None), pad=None, key=None, tooltip=None):
'''
Image Element
:param filename:
:param size: Size of field in characters
'''
self.Filename = filename
self.Data = data
self.tktext_label = None
if data is None and filename is None:
print('* Warning... no image specified in Image Element! *')
super().__init__(ELEM_TYPE_IMAGE, size=size, pad=pad, key=key, tooltip=tooltip)
return
def Update(self, filename=None, data=None):
if filename is not None:
image = tk.PhotoImage(file=filename)
elif data is not None:
if type(data) is bytes:
image = tk.PhotoImage(data=data)
else:
image = data
else: return
width, height = image.width(), image.height()
self.tktext_label.configure(image=image, width=width, height=height)
# self.tktext_label.configure(image=image)
self.tktext_label.image = image
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# Canvas #
# ---------------------------------------------------------------------- #
class Canvas(Element):
def __init__(self, canvas=None, background_color=None, size=(None, None), pad=None, key=None, tooltip=None):
self.BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self._TKCanvas = canvas
super().__init__(ELEM_TYPE_CANVAS, background_color=background_color, size=size, pad=pad, key=key, tooltip=tooltip)
return
@property
def TKCanvas(self):
if self._TKCanvas is None:
print('*** Did you forget to call Finalize()? Your code should look something like: ***')
print('*** form = sg.Window("My Form").Layout(layout).Finalize() ***')
return self._TKCanvas
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# Graph #
# ---------------------------------------------------------------------- #
class Graph(Element):
def __init__(self, canvas_size, graph_bottom_left, graph_top_right, background_color=None, pad=None, key=None, tooltip=None):
self.CanvasSize = canvas_size
self.BottomLeft = graph_bottom_left
self.TopRight = graph_top_right
self._TKCanvas = None
self._TKCanvas2 = None
super().__init__(ELEM_TYPE_GRAPH, background_color=background_color, size=canvas_size, pad=pad, key=key, tooltip=tooltip)
return
def _convert_xy_to_canvas_xy(self, x_in, y_in):
scale_x = (self.CanvasSize[0] - 0) / (self.TopRight[0] - self.BottomLeft[0])
scale_y = (0 - self.CanvasSize[1]) / (self.TopRight[1] - self.BottomLeft[1])
new_x = 0 + scale_x * (x_in - self.BottomLeft[0])
new_y = self.CanvasSize[1] + scale_y * (y_in - self.BottomLeft[1])
return new_x, new_y
def DrawLine(self, point_from, point_to, color='black', width=1):
converted_point_from = self._convert_xy_to_canvas_xy(point_from[0], point_from[1])
converted_point_to = self._convert_xy_to_canvas_xy(point_to[0], point_to[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
return self._TKCanvas2.create_line(converted_point_from, converted_point_to, width=width, fill=color)
def DrawPoint(self, point, size=2, color='black'):
converted_point = self._convert_xy_to_canvas_xy(point[0], point[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
return self._TKCanvas2.create_oval(converted_point[0]-size, converted_point[1]-size, converted_point[0]+size, converted_point[1]+size, fill=color, outline=color )
def DrawCircle(self, center_location, radius, fill_color=None, line_color='black'):
converted_point = self._convert_xy_to_canvas_xy(center_location[0], center_location[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
return self._TKCanvas2.create_oval(converted_point[0]-radius, converted_point[1]-radius, converted_point[0]+radius, converted_point[1]+radius, fill=fill_color, outline=line_color)
def DrawOval(self, top_left, bottom_right, fill_color=None, line_color=None):
converted_top_left = self._convert_xy_to_canvas_xy(top_left[0], top_left[1])
converted_bottom_right = self._convert_xy_to_canvas_xy(bottom_right[0],bottom_right[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
return self._TKCanvas2.create_oval(converted_top_left[0], converted_top_left[1], converted_bottom_right[0], converted_bottom_right[1], fill=fill_color, outline=line_color)
def DrawRectangle(self, top_left, bottom_right, fill_color=None, line_color=None):
converted_top_left = self._convert_xy_to_canvas_xy(top_left[0], top_left[1] )
converted_bottom_right = self._convert_xy_to_canvas_xy(bottom_right[0], bottom_right[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
return self._TKCanvas2.create_rectangle(converted_top_left[0], converted_top_left[1], converted_bottom_right[0], converted_bottom_right[1], fill=fill_color, outline=line_color)
def DrawText(self, text, location, color='black', font=None, angle=0):
converted_point = self._convert_xy_to_canvas_xy(location[0], location[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
text_id = self._TKCanvas2.create_text(converted_point[0], converted_point[1], text=text, font=font, fill=color, angle=angle)
return text_id
def Erase(self):
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
self._TKCanvas2.delete('all')
def Update(self, background_color):
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
self._TKCanvas2.configure(background=background_color)
def Move(self, x_direction, y_direction):
zero_converted = self._convert_xy_to_canvas_xy(0,0)
shift_converted = self._convert_xy_to_canvas_xy(x_direction, y_direction)
shift_amount = (shift_converted[0]-zero_converted[0], shift_converted[1]-zero_converted[1])
if self._TKCanvas2 is None:
print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***')
print('Call Window.Finalize() prior to this operation')
return None
self._TKCanvas2.move('all', shift_amount[0], shift_amount[1])
def MoveFigure(self, figure, x_direction, y_direction):
zero_converted = self._convert_xy_to_canvas_xy(0,0)
shift_converted = self._convert_xy_to_canvas_xy(x_direction, y_direction)
shift_amount = (shift_converted[0]-zero_converted[0], shift_converted[1]-zero_converted[1])
if figure is None:
print('*** WARNING - Your figure is None. It most likely means your did not Finalize your Window ***')
print('Call Window.Finalize() prior to all graph operations')
return None
self._TKCanvas2.move(figure, shift_amount[0], shift_amount[1])
@property
def TKCanvas(self):
if self._TKCanvas2 is None:
print('*** Did you forget to call Finalize()? Your code should look something like: ***')
print('*** form = sg.Window("My Form").Layout(layout).Finalize() ***')
return self._TKCanvas2
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# Frame #
# ---------------------------------------------------------------------- #
class Frame(Element):
def __init__(self, title, layout, title_color=None, background_color=None, title_location=None , relief=DEFAULT_FRAME_RELIEF, size=(None, None), font=None, pad=None, border_width=None, key=None, tooltip=None):
self.UseDictionary = False
self.ReturnValues = None
self.ReturnValuesList = []
self.ReturnValuesDictionary = {}
self.DictionaryKeyCounter = 0
self.ParentWindow = None
self.Rows = []
# self.ParentForm = None
self.TKFrame = None
self.Title = title
self.Relief = relief
self.TitleLocation = title_location
self.BorderWidth = border_width
self.BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self.Layout(layout)
super().__init__(ELEM_TYPE_FRAME, background_color=background_color, text_color=title_color, size=size, font=font, pad=pad, key=key, tooltip=tooltip)
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)
element.ParentContainer = self
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 _GetElementAtLocation(self, location):
(row_num,col_num) = location
row = self.Rows[row_num]
element = row[col_num]
return element
def __del__(self):
for row in self.Rows:
for element in row:
element.__del__()
super().__del__()
# ---------------------------------------------------------------------- #
# Tab #
# ---------------------------------------------------------------------- #
class Tab(Element):
def __init__(self, title, layout, title_color=None, background_color=None, font=None, pad=None, border_width=None, key=None, tooltip=None):
self.UseDictionary = False
self.ReturnValues = None
self.ReturnValuesList = []
self.ReturnValuesDictionary = {}
self.DictionaryKeyCounter = 0
self.ParentWindow = None
self.Rows = []
self.TKFrame = None
self.Title = title
self.BorderWidth = border_width
self.BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self.Layout(layout)
super().__init__(ELEM_TYPE_TAB, background_color=background_color, text_color=title_color, font=font, pad=pad, key=key, tooltip=tooltip)
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)
element.ParentContainer = self
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 _GetElementAtLocation(self, location):
(row_num,col_num) = location
row = self.Rows[row_num]
element = row[col_num]
return element
def __del__(self):
for row in self.Rows:
for element in row:
element.__del__()
super().__del__()
# ---------------------------------------------------------------------- #
# TabGroup #
# ---------------------------------------------------------------------- #
class TabGroup(Element):
def __init__(self, layout, title_color=None, background_color=None, font=None, change_submits=False, pad=None, border_width=None, key=None, tooltip=None):
self.UseDictionary = False
self.ReturnValues = None
self.ReturnValuesList = []
self.ReturnValuesDictionary = {}
self.DictionaryKeyCounter = 0
self.ParentWindow = None
self.Rows = []
self.TKNotebook = None
self.BorderWidth = border_width
self.BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self.ChangeSubmits = change_submits
self.Layout(layout)
super().__init__(ELEM_TYPE_TAB_GROUP, background_color=background_color, text_color=title_color, font=font, pad=pad, key=key, tooltip=tooltip)
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)
element.ParentContainer = self
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 _GetElementAtLocation(self, location):
(row_num,col_num) = location
row = self.Rows[row_num]
element = row[col_num]
return element
def FindKeyFromTabName(self, tab_name):
for row in self.Rows:
for element in row:
if element.Title == tab_name:
return element.Key
return None
def __del__(self):
for row in self.Rows:
for element in row:
element.__del__()
super().__del__()
# ---------------------------------------------------------------------- #
# Slider #
# ---------------------------------------------------------------------- #
class Slider(Element):
def __init__(self, range=(None,None), default_value=None, resolution=None, orientation=None, border_width=None, relief=None, change_submits=False, size=(None, None), font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
'''
Slider
:param range:
:param default_value:
:param resolution:
:param orientation:
:param border_width:
:param relief:
:param change_submits:
:param scale:
:param size:
:param font:
:param background_color:
:param text_color:
:param key:
:param pad:
'''
self.TKScale = None
self.Range = (1,10) if range == (None, None) else range
self.DefaultValue = self.Range[0] 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
self.Resolution = 1 if resolution is None else resolution
self.ChangeSubmits = change_submits
super().__init__(ELEM_TYPE_INPUT_SLIDER, size=size, font=font, background_color=background_color, text_color=text_color, key=key, pad=pad, tooltip=tooltip)
return
def Update(self, value=None, range=(None, None), disabled=None):
if value is not None:
try:
self.TKIntVar.set(value)
if range != (None, None):
self.TKScale.config(from_ = range[0], to_ = range[1])
except: pass
self.DefaultValue = value
if disabled == True:
self.TKScale['state'] = 'disabled'
elif disabled == False:
self.TKScale['state'] = 'normal'
def SliderChangedHandler(self, event):
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# TkScrollableFrame (Used by Column) #
# ---------------------------------------------------------------------- #
class TkScrollableFrame(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
# create a canvas object and a vertical scrollbar for scrolling it
self.vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
self.vscrollbar.pack(side='right', fill="y", expand="false")
self.hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
self.hscrollbar.pack(side='bottom', fill="x", expand="false")
self.canvas = tk.Canvas(self, yscrollcommand=self.vscrollbar.set, xscrollcommand=self.hscrollbar.set)
self.canvas.pack(side="left", fill="both", expand=True)
self.vscrollbar.config(command=self.canvas.yview)
self.hscrollbar.config(command=self.canvas.xview)
# reset the view
self.canvas.xview_moveto(0)
self.canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.TKFrame = tk.Frame(self.canvas, **kwargs)
self.frame_id = self.canvas.create_window(0, 0, window=self.TKFrame, anchor="nw")
self.canvas.config(borderwidth=0, highlightthickness=0)
self.TKFrame.config(borderwidth=0, highlightthickness=0)
self.config(borderwidth=0, highlightthickness=0)
self.bind('<Configure>', self.set_scrollregion)
self.bind_mouse_scroll(self.canvas, self.yscroll)
self.bind_mouse_scroll(self.hscrollbar, self.xscroll)
self.bind_mouse_scroll(self.vscrollbar, self.yscroll)
def resize_frame(self, e):
self.canvas.itemconfig(self.frame_id, height=e.height, width=e.width)
def yscroll(self, event):
if event.num == 5 or event.delta < 0:
self.canvas.yview_scroll(1, "unit")
elif event.num == 4 or event.delta > 0:
self.canvas.yview_scroll(-1, "unit")
def xscroll(self, event):
if event.num == 5 or event.delta < 0:
self.canvas.xview_scroll(1, "unit")
elif event.num == 4 or event.delta > 0:
self.canvas.xview_scroll(-1, "unit")
def bind_mouse_scroll(self, parent, mode):
# ~~ Windows only
parent.bind("<MouseWheel>", mode)
# ~~ Unix only
parent.bind("<Button-4>", mode)
parent.bind("<Button-5>", mode)
def set_scrollregion(self, event=None):
""" Set the scroll region on the canvas"""
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
# ---------------------------------------------------------------------- #
# Column #
# ---------------------------------------------------------------------- #
class Column(Element):
def __init__(self, layout, background_color = None, size=(None, None), pad=None, scrollable=False, key=None):
self.UseDictionary = False
self.ReturnValues = None
self.ReturnValuesList = []
self.ReturnValuesDictionary = {}
self.DictionaryKeyCounter = 0
self.ParentWindow = None
self.Rows = []
# self.ParentForm = None
self.TKFrame = None
self.Scrollable = scrollable
bg = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self.Layout(layout)
super().__init__(ELEM_TYPE_COLUMN, background_color=background_color, size=size, pad=pad, key=key)
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)
element.ParentContainer = self
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 _GetElementAtLocation(self, location):
(row_num,col_num) = location
row = self.Rows[row_num]
element = row[col_num]
return element
def __del__(self):
for row in self.Rows:
for element in row:
element.__del__()
try:
del(self.TKFrame)
except:
pass
super().__del__()
# ---------------------------------------------------------------------- #
# Calendar #
# ---------------------------------------------------------------------- #
class TKCalendar(tkinter.ttk.Frame):
"""
This code was shamelessly lifted from moshekaplan's repository - moshekaplan/tkinter_components
"""
# XXX ToDo: cget and configure
datetime = calendar.datetime.datetime
timedelta = calendar.datetime.timedelta
def __init__(self, master=None, target_element=None, **kw):
"""
WIDGET-SPECIFIC OPTIONS
locale, firstweekday, year, month, selectbackground,
selectforeground
"""
self._TargetElement = target_element
# remove custom options from kw before initializating ttk.Frame
fwday = kw.pop('firstweekday', calendar.MONDAY)
year = kw.pop('year', self.datetime.now().year)
month = kw.pop('month', self.datetime.now().month)
locale = kw.pop('locale', None)
sel_bg = kw.pop('selectbackground', '#ecffc4')
sel_fg = kw.pop('selectforeground', '#05640e')
self._date = self.datetime(year, month, 1)
self._selection = None # no date selected
tkinter.ttk.Frame.__init__(self, master, **kw)
# instantiate proper calendar class
if locale is None:
self._cal = calendar.TextCalendar(fwday)
else:
self._cal = calendar.LocaleTextCalendar(fwday, locale)
self.__setup_styles() # creates custom styles
self.__place_widgets() # pack/grid used widgets
self.__config_calendar() # adjust calendar columns and setup tags
# configure a canvas, and proper bindings, for selecting dates
self.__setup_selection(sel_bg, sel_fg)
# store items ids, used for insertion later
self._items = [self._calendar.insert('', 'end', values='')
for _ in range(6)]
# insert dates in the currently empty calendar
self._build_calendar()
def __setitem__(self, item, value):
if item in ('year', 'month'):
raise AttributeError("attribute '%s' is not writeable" % item)
elif item == 'selectbackground':
self._canvas['background'] = value
elif item == 'selectforeground':
self._canvas.itemconfigure(self._canvas.text, item=value)
else:
tkinter.ttk.Frame.__setitem__(self, item, value)
def __getitem__(self, item):
if item in ('year', 'month'):
return getattr(self._date, item)
elif item == 'selectbackground':
return self._canvas['background']
elif item == 'selectforeground':
return self._canvas.itemcget(self._canvas.text, 'fill')
else:
r = tkinter.ttk.tclobjs_to_py({item: tkinter.ttk.Frame.__getitem__(self, item)})
return r[item]
def __setup_styles(self):
# custom ttk styles
style = tkinter.ttk.Style(self.master)
arrow_layout = lambda dir: (
[('Button.focus', {'children': [('Button.%sarrow' % dir, None)]})]
)
style.layout('L.TButton', arrow_layout('left'))
style.layout('R.TButton', arrow_layout('right'))
def __place_widgets(self):
# header frame and its widgets
hframe = tkinter.ttk.Frame(self)
lbtn = tkinter.ttk.Button(hframe, style='L.TButton', command=self._prev_month)
rbtn = tkinter.ttk.Button(hframe, style='R.TButton', command=self._next_month)
self._header = tkinter.ttk.Label(hframe, width=15, anchor='center')
# the calendar
self._calendar = tkinter.ttk.Treeview(self, show='', selectmode='none', height=7)
# pack the widgets
hframe.pack(in_=self, side='top', pady=4, anchor='center')
lbtn.grid(in_=hframe)
self._header.grid(in_=hframe, column=1, row=0, padx=12)
rbtn.grid(in_=hframe, column=2, row=0)
self._calendar.pack(in_=self, expand=1, fill='both', side='bottom')
def __config_calendar(self):
cols = self._cal.formatweekheader(3).split()
self._calendar['columns'] = cols
self._calendar.tag_configure('header', background='grey90')
self._calendar.insert('', 'end', values=cols, tag='header')
# adjust its columns width
font = tkinter.font.Font()
maxwidth = max(font.measure(col) for col in cols)
for col in cols:
self._calendar.column(col, width=maxwidth, minwidth=maxwidth,
anchor='e')
def __setup_selection(self, sel_bg, sel_fg):
self._font = tkinter.font.Font()
self._canvas = canvas = tk.Canvas(self._calendar,
background=sel_bg, borderwidth=0, highlightthickness=0)
canvas.text = canvas.create_text(0, 0, fill=sel_fg, anchor='w')
canvas.bind('<ButtonPress-1>', lambda evt: canvas.place_forget())
self._calendar.bind('<Configure>', lambda evt: canvas.place_forget())
self._calendar.bind('<ButtonPress-1>', self._pressed)
def __minsize(self, evt):
width, height = self._calendar.master.geometry().split('x')
height = height[:height.index('+')]
self._calendar.master.minsize(width, height)
def _build_calendar(self):
year, month = self._date.year, self._date.month
# update header text (Month, YEAR)
header = self._cal.formatmonthname(year, month, 0)
self._header['text'] = header.title()
# update calendar shown dates
cal = self._cal.monthdayscalendar(year, month)
for indx, item in enumerate(self._items):
week = cal[indx] if indx < len(cal) else []
fmt_week = [('%02d' % day) if day else '' for day in week]
self._calendar.item(item, values=fmt_week)
def _show_selection(self, text, bbox):
"""Configure canvas for a new selection."""
x, y, width, height = bbox
textw = self._font.measure(text)
canvas = self._canvas
canvas.configure(width=width, height=height)
canvas.coords(canvas.text, width - textw, height / 2 - 1)
canvas.itemconfigure(canvas.text, text=text)
canvas.place(in_=self._calendar, x=x, y=y)
# Callbacks
def _pressed(self, evt):
"""Clicked somewhere in the calendar."""
x, y, widget = evt.x, evt.y, evt.widget
item = widget.identify_row(y)
column = widget.identify_column(x)
if not column or not item in self._items:
# clicked in the weekdays row or just outside the columns
return
item_values = widget.item(item)['values']
if not len(item_values): # row is empty for this month
return
text = item_values[int(column[1]) - 1]
if not text: # date is empty
return
bbox = widget.bbox(item, column)
if not bbox: # calendar not visible yet
return
# update and then show selection
text = '%02d' % text
self._selection = (text, item, column)
self._show_selection(text, bbox)
year, month = self._date.year, self._date.month
try:
self._TargetElement.Update(self.datetime(year, month, int(self._selection[0])))
except: pass
def _prev_month(self):
"""Updated calendar to show the previous month."""
self._canvas.place_forget()
self._date = self._date - self.timedelta(days=1)
self._date = self.datetime(self._date.year, self._date.month, 1)
self._build_calendar() # reconstuct calendar
def _next_month(self):
"""Update calendar to show the next month."""
self._canvas.place_forget()
year, month = self._date.year, self._date.month
self._date = self._date + self.timedelta(
days=calendar.monthrange(year, month)[1] + 1)
self._date = self.datetime(self._date.year, self._date.month, 1)
self._build_calendar() # reconstruct calendar
# Properties
@property
def selection(self):
"""Return a datetime representing the current selected date."""
if not self._selection:
return None
year, month = self._date.year, self._date.month
return self.datetime(year, month, int(self._selection[0]))
# ---------------------------------------------------------------------- #
# Menu #
# ---------------------------------------------------------------------- #
class Menu(Element):
def __init__(self, menu_definition, background_color=None, size=(None, None), tearoff=True, pad=None, key=None):
self.BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self.MenuDefinition = menu_definition
self.TKMenu = None
self.Tearoff = tearoff
super().__init__(ELEM_TYPE_MENUBAR, background_color=background_color, size=size, pad=pad, key=key)
return
def MenuItemChosenCallback(self, item_chosen):
# print('IN MENU ITEM CALLBACK', item_chosen)
self.ParentForm.LastButtonClicked = item_chosen
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# Table #
# ---------------------------------------------------------------------- #
class Table(Element):
def __init__(self, values, headings=None, visible_column_map=None, col_widths=None, def_col_width=10, auto_size_columns=True, max_col_width=20, select_mode=None, display_row_numbers=False, scrollable=None, font=None, justification='right', text_color=None, background_color=None, size=(None, None), pad=None, key=None, tooltip=None):
self.Values = values
self.ColumnHeadings = headings
self.ColumnsToDisplay = visible_column_map
self.ColumnWidths = col_widths
self.MaxColumnWidth = max_col_width
self.DefaultColumnWidth = def_col_width
self.AutoSizeColumns = auto_size_columns
self.BackgroundColor = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR
self.TextColor = text_color
self.Justification = justification
self.Scrollable = scrollable
self.InitialState = None
self.SelectMode = select_mode
self.DisplayRowNumbers = display_row_numbers
self.TKTreeview = None
super().__init__(ELEM_TYPE_TABLE, text_color=text_color, background_color=background_color, font=font, size=size, pad=pad, key=key, tooltip=tooltip)
return
def __del__(self):
super().__del__()
# ---------------------------------------------------------------------- #
# Error Element #
# ---------------------------------------------------------------------- #
class ErrorElement(Element):
def __init__(self, key=None):
self.Key = key
super().__init__(ELEM_TYPE_ERROR, key=key)
return
def Update(self, *args, **kwargs):
PopupError('Keyword error in Update',
'You need to stop this madness and check your spelling',
'Bad key = {}'.format(self.Key),
'Your bad line of code may resemble this:',
'window.FindElement("{}")'.format(self.Key))
return self
def MenuItemChosenCallback(self, item_chosen):
# print('IN MENU ITEM CALLBACK', item_chosen)
self.ParentForm.LastButtonClicked = item_chosen
self.ParentForm.FormRemainedOpen = True
self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def __del__(self):
super().__del__()
# ------------------------------------------------------------------------- #
# Window CLASS #
# ------------------------------------------------------------------------- #
class Window(object):
'''
Display a user defined for and return the filled in data
'''
def __init__(self, title, default_element_size=DEFAULT_ELEMENT_SIZE, default_button_element_size = (None, None), auto_size_text=None, auto_size_buttons=None, location=(None, None), button_color=None, font=None, progress_bar_color=(None, None), background_color=None, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON, force_toplevel = False, return_keyboard_events=False, use_default_focus=True, text_justification=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False):
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
self.Title = title
self.Rows = [] # a list of ELEMENTS for this row
self.DefaultElementSize = default_element_size
self.DefaultButtonElementSize = default_button_element_size if default_button_element_size != (None, None) else DEFAULT_BUTTON_ELEMENT_SIZE
self.Location = location
self.ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR
self.BackgroundColor = background_color if background_color else DEFAULT_BACKGROUND_COLOR
self.ParentWindow = None
self.Font = font if font else DEFAULT_FONT
self.RadioDict = {}
self.BorderDepth = border_depth
self.WindowIcon = icon if icon is not None else _my_windows.user_defined_icon
self.AutoClose = auto_close
self.NonBlocking = False
self.TKroot = None
self.TKrootDestroyed = False
self.FormRemainedOpen = False
self.TKAfterID = None
self.ProgressBarColor = progress_bar_color
self.AutoCloseDuration = auto_close_duration
self.UberParent = None
self.RootNeedsDestroying = False
self.Shown = False
self.ReturnValues = None
self.ReturnValuesList = []
self.ReturnValuesDictionary = {}
self.DictionaryKeyCounter = 0
self.LastButtonClicked = None
self.LastButtonClickedWasRealtime = False
self.UseDictionary = False
self.UseDefaultFocus = use_default_focus
self.ReturnKeyboardEvents = return_keyboard_events
self.LastKeyboardEvent = None
self.TextJustification = text_justification
self.NoTitleBar = no_titlebar
self.GrabAnywhere = grab_anywhere
self.KeepOnTop = keep_on_top
self.ForceTopLevel = force_toplevel
# ------------------------- Add ONE Row to Form ------------------------- #
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)
element.ParentContainer = self
CurrentRow.append(element)
# ------------------------- 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)
def Layout(self,rows):
self.AddRows(rows)
return self
def LayoutAndRead(self,rows, non_blocking=False):
self.AddRows(rows)
self.Show(non_blocking=non_blocking)
return self.ReturnValues
def LayoutAndShow(self, rows):
raise DeprecationWarning('LayoutAndShow is no longer supported... change your call to LayoutAndRead')
# ------------------------- ShowForm THIS IS IT! ------------------------- #
def Show(self, non_blocking=False):
self.Shown = True
# Compute num rows & num cols (it'll come in handy debugging)
self.NumRows = len(self.Rows)
self.NumCols = max(len(row) for row in self.Rows)
self.NonBlocking=non_blocking
# 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
try:
if element.Key is not None:
self.UseDictionary = True
except:
pass
if not found_focus and self.UseDefaultFocus:
self.UseDefaultFocus = True
else:
self.UseDefaultFocus = False
# -=-=-=-=-=-=-=-=- RUN the GUI -=-=-=-=-=-=-=-=- ##
StartupTK(self)
# 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)
return self.ReturnValues
# ------------------------- SetIcon - set the window's fav icon ------------------------- #
def SetIcon(self, icon):
self.WindowIcon = icon
try:
self.TKroot.iconbitmap(icon)
except: pass
def _GetElementAtLocation(self, location):
(row_num,col_num) = location
row = self.Rows[row_num]
element = row[col_num]
return element
def _GetDefaultElementSize(self):
return self.DefaultElementSize
def _AutoCloseAlarmCallback(self):
try:
if self.UberParent:
window = self.UberParent
else:
window = self
if window:
window._Close()
self.TKroot.quit()
self.RootNeedsDestroying = True
except:
pass
def Read(self):
self.NonBlocking = False
if self.TKrootDestroyed:
return None, None
if not self.Shown:
self.Show()
else:
InitializeResults(self)
self.TKroot.mainloop()
if self.RootNeedsDestroying:
self.TKroot.destroy()
_my_windows.Decrement()
# 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()
if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None:
return BuildResults(self, False, self)
else:
return self.ReturnValues
def ReadNonBlocking(self, Message=''):
if self.TKrootDestroyed:
return None, None
if not self.Shown:
self.Show(non_blocking=True)
if Message:
print(Message)
try:
rc = self.TKroot.update()
except:
self.TKrootDestroyed = True
_my_windows.Decrement()
# return None, None
return BuildResults(self, False, self)
def Finalize(self):
if self.TKrootDestroyed:
return self
if not self.Shown:
self.Show(non_blocking=True)
try:
rc = self.TKroot.update()
except:
self.TKrootDestroyed = True
_my_windows.Decrement()
# return None, None
return self
def Refresh(self):
if self.TKrootDestroyed:
return self
try:
rc = self.TKroot.update()
except:
pass
return self
def Fill(self, values_dict):
FillFormWithValues(self, values_dict)
return self
def FindElement(self, key):
element = _FindElementFromKeyInSubForm(self, key)
if element is None:
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
if element is None:
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
PopupError('Keyword error in FindElement Call',
'Bad key = {}'.format(key),
'Your bad line of code may resemble this:',
'window.FindElement("{}")'.format(key))
return ErrorElement(key=key)
return element
def SaveToDisk(self, filename):
try:
results = BuildResults(self, False, self)
with open(filename, 'wb') as sf:
pickle.dump(results[1], sf)
except:
print('*** Error saving form to disk ***')
def LoadFromDisk(self, filename):
try:
with open(filename, 'rb') as df:
self.Fill(pickle.load(df))
except:
print('*** Error loading form to disk ***')
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
def StartMove(self, event):
try:
self.TKroot.x = event.x
self.TKroot.y = event.y
except: pass
def StopMove(self, event):
try:
self.TKroot.x = None
self.TKroot.y = None
except: pass
def OnMotion(self, event):
try:
deltax = event.x - self.TKroot.x
deltay = event.y - self.TKroot.y
x = self.TKroot.winfo_x() + deltax
y = self.TKroot.winfo_y() + deltay
self.TKroot.geometry("+%s+%s" % (x, y))
except:
pass
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:
BuildResults(self, False, self)
self.TKroot.quit()
def _MouseWheelCallback(self, event ):
self.LastButtonClicked = None
self.FormRemainedOpen = True
self.LastKeyboardEvent = 'MouseWheel:Down' if event.delta < 0 else 'MouseWheel:Up'
if not self.NonBlocking:
BuildResults(self, False, self)
self.TKroot.quit()
def _Close(self):
try:
self.TKroot.update()
except: pass
if not self.NonBlocking:
BuildResults(self, False, self)
if self.TKrootDestroyed:
return None
self.TKrootDestroyed = True
self.RootNeedsDestroying = True
return None
def CloseNonBlocking(self):
if self.TKrootDestroyed:
return
try:
self.TKroot.destroy()
_my_windows.Decrement()
except: pass
CloseNonBlockingForm = CloseNonBlocking
def OnClosingCallback(self):
return
def Disable(self):
self.TKroot.grab_set_global()
def Enable(self):
self.TKroot.grab_release()
def Hide(self):
self.TKroot.withdraw()
def UnHide(self):
self.TKroot.deiconify()
def __enter__(self):
return self
def __exit__(self, *a):
self.__del__()
return False
def __del__(self):
for row in self.Rows:
for element in row:
element.__del__()
# try:
# del(self.TKroot)
# except:
# pass
FlexForm = Window
# ------------------------------------------------------------------------- #
# UberForm CLASS #
# Used to make forms into TABS (it's trick) #
# ------------------------------------------------------------------------- #
class UberForm(object):
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
self.FormStayedOpen = False
def AddForm(self, form):
self.FormList.append(form)
def _Close(self):
self.FormReturnValues = []
for form in self.FormList:
form._Close()
self.FormReturnValues.append(form.ReturnValues)
if not self.TKrootDestroyed:
self.TKrootDestroyed = True
self.TKroot.destroy()
_my_windows.Decrement()
def __del__(self):
return
# =========================================================================== #
# Button Lazy Functions so the caller doesn't have to define a bunch of stuff #
# =========================================================================== #
# ------------------------- FOLDER BROWSE Element lazy function ------------------------- #
def FolderBrowse(button_text='Browse', target=(ThisRow, -1), initial_folder=None, tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_BROWSE_FOLDER, target=target, initial_folder=initial_folder, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, pad=pad, key=key)
# ------------------------- FILE BROWSE Element lazy function ------------------------- #
def FileBrowse( button_text='Browse',target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), initial_folder=None, tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_BROWSE_FILE, target=target, file_types=file_types,initial_folder=initial_folder, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, pad=pad, key=key)
# ------------------------- FILES BROWSE Element (Multiple file selection) lazy function ------------------------- #
def FilesBrowse(button_text='Browse',target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), initial_folder=None, tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_BROWSE_FILES, target=target, file_types=file_types, initial_folder=initial_folder, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, pad=pad, key=key)
# ------------------------- FILE BROWSE Element lazy function ------------------------- #
def FileSaveAs(button_text='Save As...',target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),), initial_folder=None, tooltip=None,size=(None, None), auto_size_button=None, button_color=None, font=None, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_SAVEAS_FILE, target=target, file_types=file_types, initial_folder=initial_folder, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, pad=pad, key=key)
# ------------------------- SAVE AS Element lazy function ------------------------- #
def SaveAs(button_text='Save As...',target=(ThisRow, -1), file_types=(("ALL Files", "*.*"),),initial_folder=None,tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_SAVEAS_FILE, target=target, file_types=file_types, initial_folder=initial_folder, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, pad=pad, key=key)
# ------------------------- SAVE BUTTON Element lazy function ------------------------- #
def Save(button_text='Save', size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, tooltip=None, font=None, focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color,font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- SUBMIT BUTTON Element lazy function ------------------------- #
def Submit(button_text='Submit', size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, tooltip=None, font=None, focus=False, pad=None, key=None):
return Button( button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color,font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- OPEN BUTTON Element lazy function ------------------------- #
def Open(button_text='Open', size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, tooltip=None, font=None, focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color,font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- OK BUTTON Element lazy function ------------------------- #
def OK(button_text='OK', size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, tooltip=None, font=None,focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color,font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- YES BUTTON Element lazy function ------------------------- #
def Ok(button_text='Ok', size=(None, None), auto_size_button=None, button_color=None, bind_return_key=True, tooltip=None, font=None,focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- CANCEL BUTTON Element lazy function ------------------------- #
def Cancel(button_text='Cancel', size=(None, None), auto_size_button=None, button_color=None, tooltip=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- QUIT BUTTON Element lazy function ------------------------- #
def Quit(button_text='Quit', size=(None, None), auto_size_button=None, button_color=None, tooltip=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- Exit BUTTON Element lazy function ------------------------- #
def Exit(button_text='Exit', size=(None, None), auto_size_button=None, button_color=None, tooltip=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- YES BUTTON Element lazy function ------------------------- #
def Yes(button_text='Yes', size=(None, None), auto_size_button=None, button_color=None, tooltip=None, font=None, bind_return_key=True, focus=False, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- NO BUTTON Element lazy function ------------------------- #
def No(button_text='No', size=(None, None), auto_size_button=None, button_color=None, tooltip=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- NO BUTTON Element lazy function ------------------------- #
def Help(button_text='Help', size=(None, None), auto_size_button=None, button_color=None,font=None,tooltip=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- GENERIC BUTTON Element lazy function ------------------------- #
def SimpleButton(button_text, image_filename=None, image_size=(None, None), image_subsample=None, border_width=None,tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_CLOSES_WIN, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- GENERIC BUTTON Element lazy function ------------------------- #
def ReadButton(button_text, image_filename=None, image_size=(None, None),image_subsample=None,border_width=None,tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button( button_text=button_text, button_type=BUTTON_TYPE_READ_FORM, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
ReadFormButton = ReadButton
RButton = ReadFormButton
# ------------------------- Realtime BUTTON Element lazy function ------------------------- #
def RealtimeButton(button_text, image_filename=None, image_size=(None, None),image_subsample=None,border_width=None,tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button( button_text=button_text,button_type=BUTTON_TYPE_REALTIME, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- Dummy BUTTON Element lazy function ------------------------- #
def DummyButton(button_text, image_filename=None, image_size=(None, None),image_subsample=None,border_width=None,tooltip=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text, button_type= BUTTON_TYPE_CLOSES_WIN_ONLY, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- Calendar Chooser Button lazy function ------------------------- #
def CalendarButton(button_text, target=(None,None), image_filename=None, image_size=(None, None), image_subsample=None,tooltip=None, border_width=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text, button_type=BUTTON_TYPE_CALENDAR_CHOOSER, target=target, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
# ------------------------- Calendar Chooser Button lazy function ------------------------- #
def ColorChooserButton(button_text, target=(None,None), image_filename=None, image_size=(None, None), image_subsample=None,tooltip=None, border_width=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, focus=False, pad=None, key=None):
return Button(button_text=button_text,button_type=BUTTON_TYPE_COLOR_CHOOSER, target=target, image_filename=image_filename, image_size=image_size, image_subsample=image_subsample, border_width=border_width, tooltip=tooltip, size=size, auto_size_button=auto_size_button, button_color=button_color, font=font, bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
##################################### ----- RESULTS ------ ##################################################
def AddToReturnDictionary(form, element, value):
if element.Key is None:
form.ReturnValuesDictionary[form.DictionaryKeyCounter] = value
element.Key = form.DictionaryKeyCounter
form.DictionaryKeyCounter += 1
else:
form.ReturnValuesDictionary[element.Key] = value
def AddToReturnList(form, value):
form.ReturnValuesList.append(value)
#----------------------------------------------------------------------------#
# ------- FUNCTION InitializeResults. Sets up form results matrix --------#
def InitializeResults(form):
BuildResults(form, True, form)
return
#===== Radio Button RadVar encoding and decoding =====#
#===== The value is simply the row * 1000 + col =====#
def DecodeRadioRowCol(RadValue):
row = RadValue//1000
col = RadValue%1000
return row,col
def EncodeRadioRowCol(row, col):
RadValue = row * 1000 + col
return RadValue
# ------- FUNCTION BuildResults. Form exiting so build the results to pass back ------- #
# format of return values is
# (Button Pressed, input_values)
def BuildResults(form, initialize_only, top_level_form):
# Results for elements are:
# TEXT - Nothing
# INPUT - Read value from TK
# Button - Button Text and position as a Tuple
# Get the initialized results so we don't have to rebuild
form.DictionaryKeyCounter = 0
form.ReturnValuesDictionary = {}
form.ReturnValuesList = []
BuildResultsForSubform(form, initialize_only, top_level_form)
if not top_level_form.LastButtonClickedWasRealtime:
top_level_form.LastButtonClicked = None
return form.ReturnValues
def BuildResultsForSubform(form, initialize_only, top_level_form):
button_pressed_text = top_level_form.LastButtonClicked
for row_num,row in enumerate(form.Rows):
for col_num, element in enumerate(row):
value = None
if element.Type == ELEM_TYPE_COLUMN:
element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
element.ReturnValuesList = []
element.ReturnValuesDictionary = {}
BuildResultsForSubform(element, initialize_only, top_level_form)
for item in element.ReturnValuesList:
AddToReturnList(top_level_form, item)
if element.UseDictionary:
top_level_form.UseDictionary = True
if element.ReturnValues[0] is not None: # if a button was clicked
button_pressed_text = element.ReturnValues[0]
if element.Type == ELEM_TYPE_FRAME:
element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
element.ReturnValuesList = []
element.ReturnValuesDictionary = {}
BuildResultsForSubform(element, initialize_only, top_level_form)
for item in element.ReturnValuesList:
AddToReturnList(top_level_form, item)
if element.UseDictionary:
top_level_form.UseDictionary = True
if element.ReturnValues[0] is not None: # if a button was clicked
button_pressed_text = element.ReturnValues[0]
if element.Type == ELEM_TYPE_TAB_GROUP:
element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
element.ReturnValuesList = []
element.ReturnValuesDictionary = {}
BuildResultsForSubform(element, initialize_only, top_level_form)
for item in element.ReturnValuesList:
AddToReturnList(top_level_form, item)
if element.UseDictionary:
top_level_form.UseDictionary = True
if element.ReturnValues[0] is not None: # if a button was clicked
button_pressed_text = element.ReturnValues[0]
if element.Type == ELEM_TYPE_TAB:
element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
element.ReturnValuesList = []
element.ReturnValuesDictionary = {}
BuildResultsForSubform(element, initialize_only, top_level_form)
for item in element.ReturnValuesList:
AddToReturnList(top_level_form, item)
if element.UseDictionary:
top_level_form.UseDictionary = True
if element.ReturnValues[0] is not None: # if a button was clicked
button_pressed_text = element.ReturnValues[0]
if not initialize_only:
if element.Type == ELEM_TYPE_INPUT_TEXT:
value=element.TKStringVar.get()
if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents:
element.TKStringVar.set('')
elif element.Type == ELEM_TYPE_INPUT_CHECKBOX:
value = element.TKIntVar.get()
value = (value != 0)
elif element.Type == ELEM_TYPE_INPUT_RADIO:
RadVar=element.TKIntVar.get()
this_rowcol = EncodeRadioRowCol(row_num,col_num)
value = RadVar == this_rowcol
elif element.Type == ELEM_TYPE_BUTTON:
if top_level_form.LastButtonClicked == element.ButtonText:
button_pressed_text = top_level_form.LastButtonClicked
if element.BType != BUTTON_TYPE_REALTIME: # Do not clear realtime buttons
top_level_form.LastButtonClicked = None
if element.BType == BUTTON_TYPE_CALENDAR_CHOOSER:
try:
value = element.TKCal.selection
except:
value = None
else:
try:
value = element.TKStringVar.get()
except:
value = None
elif element.Type == ELEM_TYPE_INPUT_COMBO:
value=element.TKStringVar.get()
elif element.Type == ELEM_TYPE_INPUT_OPTION_MENU:
value=element.TKStringVar.get()
elif element.Type == ELEM_TYPE_INPUT_LISTBOX:
try:
items=element.TKListbox.curselection()
value = [element.Values[int(item)] for item in items]
except:
value = ''
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)
if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents:
element.TKText.delete('1.0', tk.END)
except:
value = None
elif element.Type == ELEM_TYPE_TAB_GROUP:
try:
value=element.TKNotebook.tab(element.TKNotebook.index('current'))['text']
tab_key = element.FindKeyFromTabName(value)
if tab_key is not None:
value = tab_key
except:
value = None
else:
value = None
# 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\
element.Type != ELEM_TYPE_OUTPUT and element.Type != ELEM_TYPE_PROGRESS_BAR and \
element.Type!= ELEM_TYPE_COLUMN and element.Type != ELEM_TYPE_FRAME \
and element.Type != ELEM_TYPE_TAB:
AddToReturnList(form, value)
AddToReturnDictionary(top_level_form, element, value)
elif (element.Type == ELEM_TYPE_BUTTON and element.BType == BUTTON_TYPE_CALENDAR_CHOOSER and element.Target == (None,None)) or \
(element.Type == ELEM_TYPE_BUTTON and element.BType == BUTTON_TYPE_COLOR_CHOOSER and element.Target == (None,None)) or \
(element.Type == ELEM_TYPE_BUTTON and element.Key is not None and (element.BType in (BUTTON_TYPE_SAVEAS_FILE, BUTTON_TYPE_BROWSE_FILE, BUTTON_TYPE_BROWSE_FILES, BUTTON_TYPE_BROWSE_FOLDER))):
AddToReturnList(form, value)
AddToReturnDictionary(top_level_form, element, value)
# 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
try:
form.ReturnValuesDictionary.pop(None, None) # clean up dictionary include None was included
except: pass
if not form.UseDictionary:
form.ReturnValues = button_pressed_text, form.ReturnValuesList
else:
form.ReturnValues = button_pressed_text, form.ReturnValuesDictionary
return form.ReturnValues
def FillFormWithValues(form, values_dict):
FillSubformWithValues(form, values_dict)
def FillSubformWithValues(form, values_dict):
for row_num,row in enumerate(form.Rows):
for col_num, element in enumerate(row):
value = None
if element.Type == ELEM_TYPE_COLUMN:
FillSubformWithValues(element, values_dict)
if element.Type == ELEM_TYPE_FRAME:
FillSubformWithValues(element, values_dict)
if element.Type == ELEM_TYPE_TAB_GROUP:
FillSubformWithValues(element, values_dict)
if element.Type == ELEM_TYPE_TAB:
FillSubformWithValues(element, values_dict)
try:
value = values_dict[element.Key]
except:
continue
if element.Type == ELEM_TYPE_INPUT_TEXT:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_CHECKBOX:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_RADIO:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_COMBO:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_OPTION_MENU:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_LISTBOX:
element.SetValue(value)
elif element.Type == ELEM_TYPE_INPUT_SLIDER:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_MULTILINE:
element.Update(value)
elif element.Type == ELEM_TYPE_INPUT_SPIN:
element.Update(value)
elif element.Type == ELEM_TYPE_BUTTON:
element.Update(value)
def _FindElementFromKeyInSubForm(form, key):
for row_num, row in enumerate(form.Rows):
for col_num, element in enumerate(row):
if element.Type == ELEM_TYPE_COLUMN:
matching_elem = _FindElementFromKeyInSubForm(element, key)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_FRAME:
matching_elem = _FindElementFromKeyInSubForm(element, key)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_TAB_GROUP:
matching_elem = _FindElementFromKeyInSubForm(element, key)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_TAB:
matching_elem = _FindElementFromKeyInSubForm(element, key)
if matching_elem is not None:
return matching_elem
if element.Key == key:
return element
def AddMenuItem(top_menu, sub_menu_info, element, is_sub_menu=False, skip=False):
if type(sub_menu_info) is str:
if not is_sub_menu and not skip:
# print(f'Adding command {sub_menu_info}')
pos = sub_menu_info.find('&')
if pos != -1:
if pos == 0 or sub_menu_info[pos-1] != "\\":
sub_menu_info = sub_menu_info[:pos] + sub_menu_info[pos+1:]
if sub_menu_info == '---':
top_menu.add('separator')
else:
top_menu.add_command(label=sub_menu_info, underline=pos, command=lambda: Menu.MenuItemChosenCallback(element, sub_menu_info))
else:
i = 0
while i < (len(sub_menu_info)):
item = sub_menu_info[i]
if i != len(sub_menu_info) - 1:
if type(sub_menu_info[i+1]) == list:
new_menu = tk.Menu(top_menu, tearoff=element.Tearoff)
top_menu.add_cascade(label=sub_menu_info[i], menu=new_menu)
AddMenuItem(new_menu, sub_menu_info[i+1], element, is_sub_menu=True)
i += 1 # skip the next one
else:
AddMenuItem(top_menu, item, element)
else:
AddMenuItem(top_menu, item, element)
i += 1
# ------------------------------------------------------------------------------------------------------------------ #
# ===================================== TK CODE STARTS HERE ====================================================== #
# ------------------------------------------------------------------------------------------------------------------ #
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
element_type = element.Type
# Set foreground color
text_color = element.TextColor
# Determine Element size
element_size = element.Size
if (element_size == (None, None) and element_type != ELEM_TYPE_BUTTON): # user did not specify a size
element_size = toplevel_form.DefaultElementSize
elif (element_size == (None, None) and element_type == ELEM_TYPE_BUTTON):
element_size = toplevel_form.DefaultButtonElementSize
else: auto_size_text = False # if user has specified a size then it shouldn't autosize
# ------------------------- COLUMN element ------------------------- #
if element_type == ELEM_TYPE_COLUMN:
if element.Scrollable:
col_frame = TkScrollableFrame(tk_row_frame) # do not use yet! not working
PackFormIntoFrame(element, col_frame.TKFrame, toplevel_form)
col_frame.TKFrame.update()
if element.Size == (None, None): # if no size specified, use column width x column height/2
col_frame.canvas.config(width=col_frame.TKFrame.winfo_reqwidth(),height=col_frame.TKFrame.winfo_reqheight()/2)
else:
col_frame.canvas.config(width=element.Size[0],height=element.Size[1])
if not element.BackgroundColor in (None, COLOR_SYSTEM_DEFAULT):
col_frame.canvas.config(background=element.BackgroundColor)
col_frame.TKFrame.config(background=element.BackgroundColor, borderwidth =0, highlightthickness=0)
col_frame.config(background=element.BackgroundColor, borderwidth =0, highlightthickness=0)
else:
col_frame = tk.Frame(tk_row_frame)
PackFormIntoFrame(element, col_frame, toplevel_form)
col_frame.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None:
col_frame.configure(background=element.BackgroundColor, highlightbackground=element.BackgroundColor, highlightcolor=element.BackgroundColor)
# ------------------------- TEXT element ------------------------- #
elif element_type == ELEM_TYPE_TEXT:
# auto_size_text = element.AutoSizeText
display_text = element.DisplayText # text to display
if auto_size_text is False:
width, height=element_size
else:
lines = display_text.split('\n')
max_line_len = max([len(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)
if auto_size_text:
width = 0
if element.Justification is not None:
justification = element.Justification
elif toplevel_form.TextJustification is not None:
justification = toplevel_form.TextJustification
else:
justification = DEFAULT_TEXT_JUSTIFICATION
justify = tk.LEFT if justification == '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
tktext_label = tk.Label(tk_row_frame, textvariable=stringvar, width=width, height=height, justify=justify, bd=border_depth, font=font)
# Set wrap-length for text (in PIXELS) == PAIN IN THE ASS
wraplen = tktext_label.winfo_reqwidth()+40 # width of widget in Pixels
if not auto_size_text and height == 1:
wraplen = 0
# print("wraplen, width, height", wraplen, width, height)
tktext_label.configure(anchor=anchor, wraplen=wraplen) # set wrap to width of widget
if element.Relief is not None:
tktext_label.configure(relief=element.Relief)
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
tktext_label.configure(background=element.BackgroundColor)
if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
tktext_label.configure(fg=element.TextColor)
tktext_label.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1], expand=True)
element.TKText = tktext_label
if element.ClickSubmits:
tktext_label.bind('<Button-1>', element.TextClickedHandler)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- BUTTON element ------------------------- #
elif element_type == ELEM_TYPE_BUTTON:
stringvar = tk.StringVar()
element.TKStringVar = stringvar
element.Location = (row_num, col_num)
btext = element.ButtonText
btype = element.BType
if element.AutoSizeButton is not None:
auto_size = element.AutoSizeButton
else: auto_size = toplevel_form.AutoSizeButtons
if auto_size is False or element.Size[0] is not None:
width, height = element_size
else:
width = 0
height= toplevel_form.DefaultButtonElementSize[1]
if element.ButtonColor != (None, None)and element.ButtonColor != DEFAULT_BUTTON_COLOR:
bc = element.ButtonColor
elif toplevel_form.ButtonColor != (None, None) and toplevel_form.ButtonColor != DEFAULT_BUTTON_COLOR:
bc = toplevel_form.ButtonColor
else:
bc = DEFAULT_BUTTON_COLOR
border_depth = element.BorderWidth
if btype != BUTTON_TYPE_REALTIME:
tkbutton = tk.Button(tk_row_frame, text=btext, width=width, height=height,command=element.ButtonCallBack, justify=tk.LEFT, bd=border_depth, font=font)
else:
tkbutton = tk.Button(tk_row_frame, text=btext, width=width, height=height, justify=tk.LEFT, bd=border_depth, font=font)
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], activebackground=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
tkbutton.config(highlightthickness=0)
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
if width != 0:
tkbutton.configure(wraplength=wraplen+10) # set wrap to width of widget
tkbutton.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
if element.BindReturnKey:
element.TKButton.bind('<Return>', element.ReturnKeyHandler)
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()
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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 ""
if element.Justification is not None:
justification = element.Justification
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
element.TKEntry = tk.Entry(tk_row_frame, width=element_size[0], textvariable=element.TKStringVar, bd=border_depth, font=font, show=show, justify=justify)
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], expand=True, fill='x')
if element.Focus is True or (toplevel_form.UseDefaultFocus and not focus_set):
focus_set = True
element.TKEntry.focus_set()
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKEntry, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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 is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
combostyle = tkinter.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 = tkinter.ttk.Combobox(tk_row_frame, width=width, textvariable=element.TKStringVar,font=font )
if element.Size[1] != 1 and element.Size[1] is not None:
element.TKCombo.configure(height=element.Size[1])
# element.TKCombo['state']='readonly'
element.TKCombo['values'] = element.Values
if element.InitializeAsDisabled:
element.TKCombo['state'] = 'disabled'
# 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])
if element.DefaultValue:
for i, v in enumerate(element.Values):
if v == element.DefaultValue:
element.TKCombo.current(i)
break
else:
element.TKCombo.current(0)
if element.ChangeSubmits:
element.TKCombo.bind('<<ComboboxSelected>>', element.ComboboxSelectHandler)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_OPTION_MENU:
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()
default = element.DefaultValue if element.DefaultValue else element.Values[0]
element.TKStringVar.set(default)
element.TKOptionMenu = tk.OptionMenu(tk_row_frame, element.TKStringVar ,*element.Values)
element.TKOptionMenu.config(highlightthickness=0, font=font, width=width )
element.TKOptionMenu.config(borderwidth=border_depth)
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
element.TKOptionMenu.configure(background=element.BackgroundColor)
if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
element.TKOptionMenu.configure(fg=element.TextColor)
element.TKOptionMenu.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1])
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKOptionMenu, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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
listbox_frame = tk.Frame(tk_row_frame)
element.TKStringVar = tk.StringVar()
element.TKListbox= tk.Listbox(listbox_frame, height=element_size[1], width=width, selectmode=element.SelectMode, font=font)
for index, item in enumerate(element.Values):
element.TKListbox.insert(tk.END, item)
if element.DefaultValues is not None and item in element.DefaultValues:
element.TKListbox.selection_set(index)
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
element.TKListbox.configure(background=element.BackgroundColor)
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
element.TKListbox.configure(fg=text_color)
if element.ChangeSubmits:
element.TKListbox.bind('<<ListboxSelect>>', element.ListboxSelectHandler)
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])
if element.BindReturnKey:
element.TKListbox.bind('<Return>', element.ReturnKeyHandler)
element.TKListbox.bind('<Double-Button-1>', element.ReturnKeyHandler)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKListbox, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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], expand=True, fill='both')
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)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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)
if element.ChangeSubmits:
element.TKCheckbutton = tk.Checkbutton(tk_row_frame, anchor=tk.NW, text=element.Text, width=width,
variable=element.TKIntVar, bd=border_depth, font=font,
command=element.CheckboxHandler)
else:
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 and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
element.TKCheckbutton.configure(background=element.BackgroundColor)
element.TKCheckbutton.configure(selectcolor=element.BackgroundColor)
element.TKCheckbutton.configure(activebackground=element.BackgroundColor)
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
element.TKCheckbutton.configure(fg=text_color)
element.TKCheckbutton.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1])
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKCheckbutton, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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 not element.BackgroundColor in (None, COLOR_SYSTEM_DEFAULT):
element.TKRadio.configure(background=element.BackgroundColor)
element.TKRadio.configure(selectcolor=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])
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKRadio, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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)
if element.ChangeSubmits:
element.TKSpinBox.bind('<ButtonRelease-1>', element.SpinChangedHandler)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKSpinBox, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- OUTPUT element ------------------------- #
elif element_type == ELEM_TYPE_OUTPUT:
width, height = element_size
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)
element._TKOut.pack(side=tk.LEFT, expand=True, fill='both')
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element._TKOut, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- IMAGE Box element ------------------------- #
elif element_type == ELEM_TYPE_IMAGE:
if element.Filename is not None:
photo = tk.PhotoImage(file=element.Filename)
elif element.Data is not None:
photo = tk.PhotoImage(data=element.Data)
else:
photo = None
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
if photo is not None:
element.tktext_label = tk.Label(tk_row_frame, image=photo, width=width, height=height, bd=border_depth)
else:
element.tktext_label = tk.Label(tk_row_frame, 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])
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.tktext_label, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- Canvas element ------------------------- #
elif element_type == ELEM_TYPE_CANVAS:
width, height = element_size
if element._TKCanvas is None:
element._TKCanvas = tk.Canvas(tk_row_frame, width=width, height=height, bd=border_depth)
else:
element._TKCanvas.master = tk_row_frame
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
element._TKCanvas.configure(background=element.BackgroundColor, highlightthickness=0)
element._TKCanvas.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element._TKCanvas, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- Graph element ------------------------- #
elif element_type == ELEM_TYPE_GRAPH:
width, height = element_size
if element._TKCanvas is None:
element._TKCanvas = tk.Canvas(tk_row_frame, width=width, height=height, bd=border_depth)
else:
element._TKCanvas.master = tk_row_frame
element._TKCanvas2 = tk.Canvas(element._TKCanvas, width=width, height=height, bd=border_depth)
element._TKCanvas2.pack(side=tk.LEFT)
element._TKCanvas2.addtag_all('mytag')
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
element._TKCanvas2.configure(background=element.BackgroundColor, highlightthickness=0)
element._TKCanvas.configure(background=element.BackgroundColor, highlightthickness=0)
element._TKCanvas.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element._TKCanvas, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- MENUBAR element ------------------------- #
elif element_type == ELEM_TYPE_MENUBAR:
menu_def = element.MenuDefinition
element.TKMenu = tk.Menu(toplevel_form.TKroot, tearoff=element.Tearoff) # create the menubar
menubar = element.TKMenu
for menu_entry in menu_def:
# print(f'Adding a Menubar ENTRY {menu_entry}')
baritem = tk.Menu(menubar, tearoff=element.Tearoff)
pos = menu_entry[0].find('&')
# print(pos)
if pos != -1:
if pos == 0 or menu_entry[0][pos-1] != "\\":
menu_entry[0] = menu_entry[0][:pos] + menu_entry[0][pos+1:]
menubar.add_cascade(label=menu_entry[0], menu=baritem, underline = pos)
if len(menu_entry) > 1:
AddMenuItem(baritem, menu_entry[1], element)
toplevel_form.TKroot.configure(menu=element.TKMenu)
# ------------------------- Frame element ------------------------- #
elif element_type == ELEM_TYPE_FRAME:
labeled_frame = tk.LabelFrame(tk_row_frame, text=element.Title, relief=element.Relief)
PackFormIntoFrame(element, labeled_frame, toplevel_form)
labeled_frame.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None:
labeled_frame.configure(background=element.BackgroundColor, highlightbackground=element.BackgroundColor, highlightcolor=element.BackgroundColor)
if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
labeled_frame.configure(foreground=element.TextColor)
if font is not None:
labeled_frame.configure(font=font)
if element.TitleLocation is not None:
labeled_frame.configure(labelanchor=element.TitleLocation)
if element.BorderWidth is not None:
labeled_frame.configure(borderwidth=element.BorderWidth)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(labeled_frame, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- Tab element ------------------------- #
elif element_type == ELEM_TYPE_TAB:
element.TKFrame = tk.Frame(form.TKNotebook)
PackFormIntoFrame(element, element.TKFrame, toplevel_form)
form.TKNotebook.add(element.TKFrame, text=element.Title)
form.TKNotebook.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None:
element.TKFrame.configure(background=element.BackgroundColor,
highlightbackground=element.BackgroundColor,
highlightcolor=element.BackgroundColor)
# if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
# element.TKFrame.configure(foreground=element.TextColor)
if element.BorderWidth is not None:
element.TKFrame.configure(borderwidth=element.BorderWidth)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKFrame, text=element.Tooltip,
timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- TabGroup element ------------------------- #
elif element_type == ELEM_TYPE_TAB_GROUP:
element.TKNotebook = tkinter.ttk.Notebook(tk_row_frame)
PackFormIntoFrame(element, toplevel_form.TKroot, toplevel_form)
# element.TKNotebook.pack(side=tk.LEFT)
# if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None:
# element.TKNotebook.configure(background=element.BackgroundColor,
# highlightbackground=element.BackgroundColor,
# highlightcolor=element.BackgroundColor)
# if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None:
# element.TKNotebook.configure(foreground=element.TextColor)
if element.ChangeSubmits:
element.TKNotebook.bind('<<NotebookTabChanged>>', element.TabGroupSelectHandler)
if element.BorderWidth is not None:
element.TKNotebook.configure(borderwidth=element.BorderWidth)
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKNotebook, text=element.Tooltip,
timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- 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]
if element.ChangeSubmits:
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, command=element.SliderChangedHandler)
else:
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)
tkscale.config(highlightthickness=0)
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
tkscale.configure(background=element.BackgroundColor)
if DEFAULT_SCROLLBAR_COLOR != COLOR_SYSTEM_DEFAULT:
tkscale.config(troughcolor=DEFAULT_SCROLLBAR_COLOR)
if text_color is not None and text_color != COLOR_SYSTEM_DEFAULT:
tkscale.configure(fg=text_color)
tkscale.pack(side=tk.LEFT, padx=element.Pad[0],pady=element.Pad[1])
element.TKScale = tkscale
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKScale, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- TABLE element ------------------------- #
elif element_type == ELEM_TYPE_TABLE:
width, height = element_size
if element.Justification == 'left':
anchor = tk.W
elif element.Justification == 'right':
anchor = tk.E
else:
anchor = tk.CENTER
column_widths = {}
for row in element.Values:
for i,col in enumerate(row):
col_width = min(len(str(col)), element.MaxColumnWidth)
try:
if col_width > column_widths[i]:
column_widths[i] = col_width
except:
column_widths[i] = col_width
if element.ColumnsToDisplay is None:
displaycolumns = element.ColumnHeadings
else:
displaycolumns = []
for i, should_display in enumerate(element.ColumnsToDisplay):
if should_display:
displaycolumns.append(element.ColumnHeadings[i])
column_headings= element.ColumnHeadings
if element.DisplayRowNumbers: # if display row number, tack on the numbers to front of columns
displaycolumns = ['Row',] + displaycolumns
column_headings = ['Row',] + element.ColumnHeadings
element.TKTreeview = tkinter.ttk.Treeview(tk_row_frame, columns=column_headings,
displaycolumns=displaycolumns, show='headings', height=height, selectmode=element.SelectMode)
treeview = element.TKTreeview
if element.DisplayRowNumbers:
treeview.heading('Row', text='Row') # make a dummy heading
treeview.column('Row', width=50, anchor=anchor)
for i, heading in enumerate(element.ColumnHeadings):
treeview.heading(heading, text=heading)
if element.AutoSizeColumns:
width = max(column_widths[i], len(heading))
else:
try:
width = element.ColumnWidths[i]
except:
width = element.DefaultColumnWidth
treeview.column(heading, width=width*CharWidthInPixels(), anchor=anchor)
for i, value in enumerate(element.Values):
if element.DisplayRowNumbers:
value = [i] + value
id = treeview.insert('', 'end', text=value, values=value)
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
tkinter.ttk.Style().configure("Treeview", background=element.BackgroundColor, fieldbackground=element.BackgroundColor)
if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
tkinter.ttk.Style().configure("Treeview", foreground=element.TextColor)
# scrollable_frame.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1], expand=True, fill='both')
element.TKTreeview.pack(side=tk.LEFT,expand=True, padx=0, pady=0, fill='both')
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
#............................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])
tk_row_frame.pack(side=tk.TOP, anchor='nw', padx=DEFAULT_MARGINS[0], expand=True)
if form.BackgroundColor is not None and form.BackgroundColor != COLOR_SYSTEM_DEFAULT:
tk_row_frame.configure(background=form.BackgroundColor)
toplevel_form.TKroot.configure(padx=DEFAULT_MARGINS[0], pady=DEFAULT_MARGINS[1])
return
def ConvertFlexToTK(MyFlexForm):
master = MyFlexForm.TKroot
# only set title on non-tabbed forms
master.title(MyFlexForm.Title)
InitializeResults(MyFlexForm)
try:
if MyFlexForm.NoTitleBar:
MyFlexForm.TKroot.wm_overrideredirect(True)
except:
pass
PackFormIntoFrame(MyFlexForm, master, MyFlexForm)
#....................................... DONE creating and laying out window ..........................#
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 to do updates or values are bad
win_width = master.winfo_width()
win_height = master.winfo_height()
x = screen_width/2 -win_width/2
y = screen_height/2 - win_height/2
if y+win_height > screen_height:
y = screen_height-win_height
if x+win_width > screen_width:
x = screen_width-win_width
move_string = '+%i+%i'%(int(x),int(y))
master.geometry(move_string)
master.update_idletasks() # don't forget
return
# ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----#
def StartupTK(my_flex_form):
global _my_windows
ow = _my_windows.NumOpenWindows
# print('Starting TK open Windows = {}'.format(ow))
if not ow and not my_flex_form.ForceTopLevel:
root = tk.Tk()
else:
root = tk.Toplevel()
try:
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
except:
pass
# root.wm_overrideredirect(True)
if my_flex_form.BackgroundColor is not None and my_flex_form.BackgroundColor != COLOR_SYSTEM_DEFAULT:
root.configure(background=my_flex_form.BackgroundColor)
_my_windows.Increment()
my_flex_form.TKroot = root
# Make moveable window
if (my_flex_form.GrabAnywhere is not False and not (my_flex_form.NonBlocking and my_flex_form.GrabAnywhere is not True)):
root.bind("<ButtonPress-1>", my_flex_form.StartMove)
root.bind("<ButtonRelease-1>", my_flex_form.StopMove)
root.bind("<B1-Motion>", my_flex_form.OnMotion)
if my_flex_form.KeepOnTop:
root.wm_attributes("-topmost", 1)
# root.protocol("WM_DELETE_WINDOW", MyFlexForm.DestroyedCallback())
# root.bind('<Destroy>', MyFlexForm.DestroyedCallback())
ConvertFlexToTK(my_flex_form)
my_flex_form.SetIcon(my_flex_form.WindowIcon)
try:
root.attributes('-alpha', 255) # hide window while building it. makes for smoother 'paint'
except:
pass
if my_flex_form.ReturnKeyboardEvents and not my_flex_form.NonBlocking:
root.bind("<KeyRelease>", my_flex_form._KeyboardCallback)
root.bind("<MouseWheel>", my_flex_form._MouseWheelCallback)
elif my_flex_form.ReturnKeyboardEvents:
root.bind("<Key>", my_flex_form._KeyboardCallback)
root.bind("<MouseWheel>", my_flex_form._MouseWheelCallback)
if my_flex_form.AutoClose:
duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form.AutoCloseDuration is None else my_flex_form.AutoCloseDuration
my_flex_form.TKAfterID = root.after(duration * 1000, my_flex_form._AutoCloseAlarmCallback)
if my_flex_form.NonBlocking:
my_flex_form.TKroot.protocol("WM_WINDOW_DESTROYED", my_flex_form.OnClosingCallback())
else: # it's a blocking form
# print('..... CALLING MainLoop')
my_flex_form.TKroot.mainloop()
# print('..... BACK from MainLoop')
if not my_flex_form.FormRemainedOpen:
_my_windows.Decrement()
if my_flex_form.RootNeedsDestroying:
my_flex_form.TKroot.destroy()
my_flex_form.RootNeedsDestroying = False
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
# ============================== 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')])
width_used = max(longest_line_len, width_used)
max_line_total = max(max_line_total, width_used)
lines_needed = _GetNumLinesNeeded(message, width_used)
total_lines += lines_needed
single_line_message += message + '\n'
return single_line_message, width_used, total_lines
# ============================== ProgressMeter =====#
# ===================================================#
def _ProgressMeter(title, max_value, *args, **_3to2kwargs):
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'border_width' in _3to2kwargs: border_width = _3to2kwargs['border_width']; del _3to2kwargs['border_width']
else: border_width = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = DEFAULT_PROGRESS_BAR_SIZE
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'bar_color' in _3to2kwargs: bar_color = _3to2kwargs['bar_color']; del _3to2kwargs['bar_color']
else: bar_color = (None,None)
if 'orientation' in _3to2kwargs: orientation = _3to2kwargs['orientation']; del _3to2kwargs['orientation']
else: orientation = None
'''
Create and show a form on tbe caller's behalf.
:param title:
:param max_value:
:param args: ANY number of arguments the caller wants to display
:param orientation:
:param bar_color:
:param size:
:param Style:
:param StyleOffset:
:return: ProgressBar object that is in the form
'''
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
bar2 = ProgressBar(max_value, orientation=local_orientation, size=size, bar_color=bar_color, border_width=local_border_width, relief=DEFAULT_PROGRESS_BAR_RELIEF)
form = Window(title, auto_size_text=True, grab_anywhere=grab_anywhere)
# Form using a horizontal bar
if local_orientation[0].lower() == 'h':
single_line_message, width, height = ConvertArgsToSingleString(*args)
bar2.TextToDisplay = single_line_message
bar2.MaxValue = max_value
bar2.CurrentValue = 0
bar_text = Text(single_line_message, size=(width, height + 3), auto_size_text=True)
form.AddRow(bar_text)
form.AddRow((bar2))
form.AddRow((Cancel(button_color=button_color)))
else:
single_line_message, width, height = ConvertArgsToSingleString(*args)
bar2.TextToDisplay = single_line_message
bar2.MaxValue = max_value
bar2.CurrentValue = 0
bar_text = Text(single_line_message, size=(width, height + 3), auto_size_text=True)
form.AddRow(bar2, bar_text)
form.AddRow((Cancel(button_color=button_color)))
form.NonBlocking = True
form.Show(non_blocking= True)
return bar2, bar_text
# ============================== ProgressMeterUpdate =====#
def _ProgressMeterUpdate(bar, value, text_elem, *args):
'''
Update the progress meter for a form
:param form: class ProgressBar
:param value: int
: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)
text_elem.Update(message)
# bar.TextToDisplay = message
bar.CurrentValue = value
rc = bar.UpdateBar(value)
if value >= bar.MaxValue or not rc:
bar.BarExpired = True
bar.ParentForm._Close()
if rc: # if update was OK but bar expired, decrement num windows
_my_windows.Decrement()
if bar.ParentForm.RootNeedsDestroying:
try:
bar.ParentForm.TKroot.destroy()
# _my_windows.Decrement()
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(object):
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
self.ParentForm = None
self.MeterID = None
self.MeterText = None
# =========================== 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 =====#
def EasyProgressMeter(title, current_value, max_value, *args, **_3to2kwargs):
if 'border_width' in _3to2kwargs: border_width = _3to2kwargs['border_width']; del _3to2kwargs['border_width']
else: border_width = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = DEFAULT_PROGRESS_BAR_SIZE
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'bar_color' in _3to2kwargs: bar_color = _3to2kwargs['bar_color']; del _3to2kwargs['bar_color']
else: bar_color = (None,None)
if 'orientation' in _3to2kwargs: orientation = _3to2kwargs['orientation']; del _3to2kwargs['orientation']
else: orientation = None
'''
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!
: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
:param args: VARIABLE number of arguements... you request it, we'll print it no matter what the item!
:param orientation:
:param bar_color:
:param size:
:param Style:
:param StyleOffset:
:return: False if should stop the meter
'''
local_border_width = DEFAULT_PROGRESS_BAR_BORDER_WIDTH if not border_width else border_width
# 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.Data = getattr(EasyProgressMeter, 'Data', EasyProgressMeterDataClass())
# if no meter currently running
if EasyProgressMeter.Data.MeterID is None: # Starting a new meter
print("Please change your call of EasyProgressMeter to use OneLineProgressMeter. EasyProgressMeter will be removed soon")
if int(current_value) >= int(max_value):
return False
del(EasyProgressMeter.Data)
EasyProgressMeter.Data = EasyProgressMeterDataClass(title, 1, int(max_value), datetime.datetime.utcnow(), [])
EasyProgressMeter.Data.ComputeProgressStats()
message = "\n".join([line for line in EasyProgressMeter.Data.StatMessages])
EasyProgressMeter.Data.MeterID, EasyProgressMeter.Data.MeterText= _ProgressMeter(title, int(max_value), message, *args, orientation=orientation, bar_color=bar_color, size=size, button_color=button_color, border_width=local_border_width)
EasyProgressMeter.Data.ParentForm = EasyProgressMeter.Data.MeterID.ParentForm
return True
# if exactly the same values as before, then ignore.
if EasyProgressMeter.Data.MaxValue == max_value and EasyProgressMeter.Data.CurrentValue == current_value:
return True
if EasyProgressMeter.Data.MaxValue != int(max_value):
EasyProgressMeter.Data.MeterID = None
EasyProgressMeter.Data.ParentForm = None
del(EasyProgressMeter.Data)
EasyProgressMeter.Data = 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
EasyProgressMeter.Data.CurrentValue = int(current_value)
EasyProgressMeter.Data.MaxValue = int(max_value)
EasyProgressMeter.Data.ComputeProgressStats()
message = ''
for line in EasyProgressMeter.Data.StatMessages:
message = message + str(line) + '\n'
message = "\n".join(EasyProgressMeter.Data.StatMessages)
args= args + (message,)
rc = _ProgressMeterUpdate(EasyProgressMeter.Data.MeterID, current_value,
EasyProgressMeter.Data.MeterText, *args)
# if counter >= max then the progress meter is all done. Indicate none running
if current_value >= EasyProgressMeter.Data.MaxValue or not rc:
EasyProgressMeter.Data.MeterID = None
del(EasyProgressMeter.Data)
EasyProgressMeter.Data = 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
def EasyProgressMeterCancel(title, *args):
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
rc = EasyProgressMeter(title, EasyProgressMeter.EasyProgressMeterData.MaxValue, EasyProgressMeter.EasyProgressMeterData.MaxValue, ' *** CANCELLING ***', 'Caller requested a cancel', *args)
return rc
return True
# global variable containing dictionary will all currently running one-line progress meters.
_one_line_progress_meters = {}
# ============================== OneLineProgressMeter =====#
def OneLineProgressMeter(title, current_value, max_value, key, *args, **_3to2kwargs):
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'border_width' in _3to2kwargs: border_width = _3to2kwargs['border_width']; del _3to2kwargs['border_width']
else: border_width = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = DEFAULT_PROGRESS_BAR_SIZE
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'bar_color' in _3to2kwargs: bar_color = _3to2kwargs['bar_color']; del _3to2kwargs['bar_color']
else: bar_color = (None,None)
if 'orientation' in _3to2kwargs: orientation = _3to2kwargs['orientation']; del _3to2kwargs['orientation']
else: orientation = None
global _one_line_progress_meters
local_border_width = DEFAULT_PROGRESS_BAR_BORDER_WIDTH if border_width is not None else border_width
try:
meter_data = _one_line_progress_meters[key]
except: # a new meater is starting
if int(current_value) >= int(max_value): # if already expired then it's an old meter, ignore
return False
meter_data = EasyProgressMeterDataClass(title, 1, int(max_value), datetime.datetime.utcnow(), [])
_one_line_progress_meters[key] = meter_data
meter_data.ComputeProgressStats()
message = "\n".join([line for line in meter_data.StatMessages])
meter_data.MeterID, meter_data.MeterText= _ProgressMeter(title, int(max_value), message, *args, orientation=orientation, bar_color=bar_color, size=size, button_color=button_color, border_width=local_border_width, grab_anywhere=grab_anywhere)
meter_data.ParentForm = meter_data.MeterID.ParentForm
return True
# if exactly the same values as before, then ignore, return success.
if meter_data.MaxValue == max_value and meter_data.CurrentValue == current_value:
return True
meter_data.CurrentValue = int(current_value)
meter_data.MaxValue = int(max_value)
meter_data.ComputeProgressStats()
message = ''
for line in meter_data.StatMessages:
message = message + str(line) + '\n'
message = "\n".join(meter_data.StatMessages)
args= args + (message,)
rc = _ProgressMeterUpdate(meter_data.MeterID, current_value,
meter_data.MeterText, *args)
# if counter >= max then the progress meter is all done. Indicate none running
if current_value >= meter_data.MaxValue or not rc:
del _one_line_progress_meters[key]
return False
return rc # return whatever the update told us
def OneLineProgressMeterCancel(key):
global _one_line_progress_meters
try:
meter_data = _one_line_progress_meters[key]
except: # meter is already deleted
return
OneLineProgressMeter('', meter_data.MaxValue, meter_data.MaxValue, key=key)
# 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
# ======================== EasyPrint =====#
# ===================================================#
_easy_print_data = None # global variable... I'm cheating
class DebugWin(object):
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 = Window('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, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = 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("")
self.form.ReadNonBlocking()
def Close(self):
self.form.CloseNonBlockingForm()
self.form.__del__()
def Print(*args, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None,None)
EasyPrint(*args, size=size, end=end, sep=sep)
def PrintClose():
EasyPrintClose()
def eprint(*args, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None,None)
EasyPrint(*args, size=size, end=end, sep=sep)
def EasyPrint(*args, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None,None)
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, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None,None)
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:
EasyPrint.easy_print_data._Close()
EasyPrint.easy_print_data = None
# del EasyPrint.easy_print_data
# ======================== Scrolled Text Box =====#
# ===================================================#
def ScrolledTextBox(*args, **_3to2kwargs):
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None, None)
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'yes_no' in _3to2kwargs: yes_no = _3to2kwargs['yes_no']; del _3to2kwargs['yes_no']
else: yes_no = False
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if not args: return
width, height = size
width = width if width else MESSAGE_BOX_LINE_WIDTH
form = Window(args[0], auto_size_text=True, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration)
max_line_total, max_line_width, total_lines, height_computed = 0,0,0,0
complete_output = ''
for message in args:
# fancy code to check if string and convert if not is not need. Just always convert to string :-)
# if not isinstance(message, str): message = str(message)
message = str(message)
longest_line_len = max([len(l) for l in message.split('\n')])
width_used = min(longest_line_len, width)
max_line_total = max(max_line_total, width_used)
max_line_width = width
lines_needed = _GetNumLinesNeeded(message, width_used)
height_computed += lines_needed
complete_output += message + '\n'
total_lines += lines_needed
height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed
if height:
height_computed = height
form.AddRow(Multiline(complete_output, size=(max_line_width, height_computed)))
pad = max_line_total-15 if max_line_total > 15 else 1
# show either an OK or Yes/No depending on paramater
if yes_no:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(), No())
button, values = form.Read()
return button
else:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Button('OK', size=(5, 1), button_color=button_color))
button, values = form.Read()
return button
PopupScrolled = ScrolledTextBox
# ---------------------------------------------------------------------- #
# 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) #
# ---------------------------------------------------------------------- #
def PopupGetFolder(message, default_path='', no_window=False, size=(None,None), button_color=None, background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False, location=(None, None)):
"""
Display popup with text entry field and browse button. Browse for folder
:param message:
:param default_path:
:param no_window:
:param size:
:param button_color:
:param background_color:
:param text_color:
:param icon:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return: Contents of text field. None if closed using X
"""
if no_window:
root = tk.Tk()
try:
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
except:
pass
folder_name = tk.filedialog.askdirectory() # show the 'get folder' dialog box
root.destroy()
return folder_name
layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)],
[InputText(default_text=default_path, size=size), FolderBrowse()],
[Ok(), Cancel()]]
window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, background_color=background_color,
font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
(button, input_values) = window.LayoutAndRead(layout)
if button != 'Ok':
return None
else:
path = input_values[0]
return path
#####################################
# PopupGetFile #
#####################################
def PopupGetFile(message, default_path='', default_extension='', save_as=False, file_types=(("ALL Files", "*.*"),), no_window=False, size=(None,None), button_color=None, background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False, location=(None,None)):
"""
Display popup with text entry field and browse button. Browse for file
:param message:
:param default_path:
:param save_as:
:param file_types:
:param no_window:
:param size:
:param button_color:
:param background_color:
:param text_color:
:param icon:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
if no_window:
root = tk.Tk()
try:
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
except:
pass
if save_as:
filename = tk.filedialog.asksaveasfilename(filetypes=file_types, defaultextension=default_extension) # show the 'get file' dialog box
else:
filename = tk.filedialog.askopenfilename(filetypes=file_types, defaultextension=default_extension) # show the 'get file' dialog box
root.destroy()
return filename
browse_button = SaveAs(file_types=file_types) if save_as else FileBrowse(file_types=file_types)
layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)],
[InputText(default_text=default_path, size=size), browse_button],
[Ok(), Cancel()]]
window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, font=font, background_color=background_color,
no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
(button, input_values) = window.Layout(layout).Read()
if button != 'Ok':
return None
else:
path = input_values[0]
return path
#####################################
# PopupGetText #
#####################################
def PopupGetText(message, default_text='', password_char='', size=(None,None), button_color=None, background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False, location=(None,None)):
"""
Display Popup with text entry field
:param message:
:param default_text:
:param password_char:
:param size:
:param button_color:
:param background_color:
:param text_color:
:param icon:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return: Text entered or None if window was closed
"""
layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color, font=font)],
[InputText(default_text=default_text, size=size, password_char=password_char)],
[Ok(), Cancel()]]
window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, no_titlebar=no_titlebar,
background_color=background_color, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
(button, input_values) = window.Layout(layout).Read()
if button != 'Ok':
return None
else:
return input_values[0]
# ============================== SetGlobalIcon ======#
# Sets the icon to be used by default #
# ===================================================#
def SetGlobalIcon(icon):
global _my_windows
try:
with open(icon, 'r') as icon_file:
pass
except:
raise FileNotFoundError
_my_windows.user_defined_icon = icon
return True
# ============================== SetOptions =========#
# Sets the icon to be used by default #
# ===================================================#
def SetOptions(icon=None, button_color=None, element_size=(None,None), button_element_size=(None, None), margins=(None,None),
element_padding=(None,None),auto_size_text=None, auto_size_buttons=None, font=None, border_width=None,
slider_border_width=None, slider_relief=None, slider_orientation=None,
autoclose_time=None, message_box_line_width=None,
progress_meter_border_depth=None, progress_meter_style=None,
progress_meter_relief=None, progress_meter_color=None, progress_meter_size=None,
text_justification=None, background_color=None, element_background_color=None,
text_element_background_color=None, input_elements_background_color=None, input_text_color=None,
scrollbar_color=None, text_color=None, element_text_color = None, debug_win_size=(None,None), window_location=(None,None),
tooltip_time=None):
global DEFAULT_ELEMENT_SIZE
global DEFAULT_BUTTON_ELEMENT_SIZE
global DEFAULT_MARGINS # Margins for each LEFT/RIGHT margin is first term
global DEFAULT_ELEMENT_PADDING # Padding between elements (row, col) in pixels
global DEFAULT_AUTOSIZE_TEXT
global DEFAULT_AUTOSIZE_BUTTONS
global DEFAULT_FONT
global DEFAULT_BORDER_WIDTH
global DEFAULT_AUTOCLOSE_TIME
global DEFAULT_BUTTON_COLOR
global MESSAGE_BOX_LINE_WIDTH
global DEFAULT_PROGRESS_BAR_BORDER_WIDTH
global DEFAULT_PROGRESS_BAR_STYLE
global DEFAULT_PROGRESS_BAR_RELIEF
global DEFAULT_PROGRESS_BAR_COLOR
global DEFAULT_PROGRESS_BAR_SIZE
global DEFAULT_TEXT_JUSTIFICATION
global DEFAULT_DEBUG_WINDOW_SIZE
global DEFAULT_SLIDER_BORDER_WIDTH
global DEFAULT_SLIDER_RELIEF
global DEFAULT_SLIDER_ORIENTATION
global DEFAULT_BACKGROUND_COLOR
global DEFAULT_INPUT_ELEMENTS_COLOR
global DEFAULT_ELEMENT_BACKGROUND_COLOR
global DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR
global DEFAULT_SCROLLBAR_COLOR
global DEFAULT_TEXT_COLOR
global DEFAULT_WINDOW_LOCATION
global DEFAULT_ELEMENT_TEXT_COLOR
global DEFAULT_INPUT_TEXT_COLOR
global DEFAULT_TOOLTIP_TIME
global _my_windows
if icon:
try:
with open(icon, 'r') as icon_file:
pass
except:
raise FileNotFoundError
_my_windows.user_defined_icon = icon
if button_color != None:
DEFAULT_BUTTON_COLOR = button_color
if element_size != (None,None):
DEFAULT_ELEMENT_SIZE = element_size
if button_element_size != (None,None):
DEFAULT_BUTTON_ELEMENT_SIZE = button_element_size
if margins != (None,None):
DEFAULT_MARGINS = margins
if element_padding != (None,None):
DEFAULT_ELEMENT_PADDING = element_padding
if auto_size_text != None:
DEFAULT_AUTOSIZE_TEXT = auto_size_text
if auto_size_buttons != None:
DEFAULT_AUTOSIZE_BUTTONS = auto_size_buttons
if font !=None:
DEFAULT_FONT = font
if border_width != None:
DEFAULT_BORDER_WIDTH = border_width
if autoclose_time != None:
DEFAULT_AUTOCLOSE_TIME = autoclose_time
if message_box_line_width != None:
MESSAGE_BOX_LINE_WIDTH = message_box_line_width
if progress_meter_border_depth != None:
DEFAULT_PROGRESS_BAR_BORDER_WIDTH = progress_meter_border_depth
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
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
if text_justification != None:
DEFAULT_TEXT_JUSTIFICATION = text_justification
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
if window_location != (None,None):
DEFAULT_WINDOW_LOCATION = window_location
if debug_win_size != (None,None):
DEFAULT_DEBUG_WINDOW_SIZE = debug_win_size
if text_color != None:
DEFAULT_TEXT_COLOR = text_color
if scrollbar_color != None:
DEFAULT_SCROLLBAR_COLOR = scrollbar_color
if element_text_color != None:
DEFAULT_ELEMENT_TEXT_COLOR = element_text_color
if input_text_color is not None:
DEFAULT_INPUT_TEXT_COLOR = input_text_color
if tooltip_time is not None:
DEFAULT_TOOLTIP_TIME = tooltip_time
return True
#################### ChangeLookAndFeel #######################
# Predefined settings that will change the colors and styles #
# of the elements. #
##############################################################
def ChangeLookAndFeel(index):
if sys.platform == 'darwin':
print('*** Changing look and feel is not supported on Mac platform ***')
return
# look and feel table
look_and_feel = {'SystemDefault': {'BACKGROUND' : COLOR_SYSTEM_DEFAULT, 'TEXT': COLOR_SYSTEM_DEFAULT, 'INPUT': COLOR_SYSTEM_DEFAULT,'TEXT_INPUT' : COLOR_SYSTEM_DEFAULT, 'SCROLL': COLOR_SYSTEM_DEFAULT, 'BUTTON': OFFICIAL_PYSIMPLEGUI_BUTTON_COLOR, 'PROGRESS': COLOR_SYSTEM_DEFAULT, 'BORDER': 1,'SLIDER_DEPTH':1, 'PROGRESS_DEPTH':0},
'GreenTan': {'BACKGROUND' : '#9FB8AD', 'TEXT': COLOR_SYSTEM_DEFAULT, 'INPUT':'#F7F3EC','TEXT_INPUT' : 'black','SCROLL': '#F7F3EC', 'BUTTON': ('white', '#475841'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'Dark': {'BACKGROUND': 'gray25', 'TEXT': 'white', 'INPUT': 'gray30',
'TEXT_INPUT': 'white', 'SCROLL': 'gray44', 'BUTTON': ('white', '#004F00'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Dark2': {'BACKGROUND': 'gray25', 'TEXT': 'white', 'INPUT': 'white',
'TEXT_INPUT': 'black', 'SCROLL': 'gray44', 'BUTTON': ('white', '#004F00'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Black': {'BACKGROUND': 'black', 'TEXT': 'white', 'INPUT': 'gray30',
'TEXT_INPUT': 'white', 'SCROLL': 'gray44', 'BUTTON': ('black', 'white'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Tan': {'BACKGROUND': '#fdf6e3', 'TEXT': '#268bd1', 'INPUT': '#eee8d5',
'TEXT_INPUT': '#6c71c3', 'SCROLL': '#eee8d5', 'BUTTON': ('white', '#063542'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'TanBlue': {'BACKGROUND': '#e5dece', 'TEXT': '#063289', 'INPUT': '#f9f8f4',
'TEXT_INPUT': '#242834', 'SCROLL': '#eee8d5', 'BUTTON': ('white', '#063289'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkTanBlue': {'BACKGROUND': '#242834', 'TEXT': '#dfe6f8', 'INPUT': '#97755c',
'TEXT_INPUT': 'white', 'SCROLL': '#a9afbb', 'BUTTON': ('white', '#063289'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkAmber': {'BACKGROUND': '#2c2825', 'TEXT': '#fdcb52', 'INPUT': '#705e52',
'TEXT_INPUT': '#fdcb52', 'SCROLL': '#705e52', 'BUTTON': ('black', '#fdcb52'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkBlue': {'BACKGROUND': '#1a2835', 'TEXT': '#d1ecff', 'INPUT': '#335267',
'TEXT_INPUT': '#acc2d0', 'SCROLL': '#1b6497', 'BUTTON': ('black', '#fafaf8'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Reds': {'BACKGROUND': '#280001', 'TEXT': 'white', 'INPUT': '#d8d584',
'TEXT_INPUT': 'black', 'SCROLL': '#763e00', 'BUTTON': ('black', '#daad28'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Green': {'BACKGROUND': '#82a459', 'TEXT': 'black', 'INPUT': '#d8d584',
'TEXT_INPUT': 'black', 'SCROLL': '#e3ecf3', 'BUTTON': ('white', '#517239'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'LightGreen' :{'BACKGROUND' : '#B7CECE', 'TEXT': 'black', 'INPUT':'#FDFFF7','TEXT_INPUT' : 'black', 'SCROLL': '#FDFFF7','BUTTON': ('white', '#658268'), 'PROGRESS':DEFAULT_PROGRESS_BAR_COLOR, 'BORDER':1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BluePurple': {'BACKGROUND' : '#A5CADD', 'TEXT': '#6E266E', 'INPUT':'#E0F5FF','TEXT_INPUT' : 'black', 'SCROLL': '#E0F5FF','BUTTON': ('white', '#303952'),'PROGRESS':DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'Purple': {'BACKGROUND': '#B0AAC2', 'TEXT': 'black', 'INPUT': '#F2EFE8','SCROLL': '#F2EFE8','TEXT_INPUT' : 'black',
'BUTTON': ('black', '#C2D4D8'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BlueMono': {'BACKGROUND': '#AAB6D3', 'TEXT': 'black', 'INPUT': '#F1F4FC','SCROLL': '#F1F4FC','TEXT_INPUT' : 'black',
'BUTTON': ('white', '#7186C7'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'GreenMono': {'BACKGROUND': '#A8C1B4', 'TEXT': 'black', 'INPUT': '#DDE0DE', 'SCROLL': '#E3E3E3','TEXT_INPUT' : 'black',
'BUTTON': ('white', '#6D9F85'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BrownBlue': {'BACKGROUND': '#64778d', 'TEXT': 'white', 'INPUT': '#f0f3f7', 'SCROLL': '#A6B2BE','TEXT_INPUT' : 'black', 'BUTTON': ('white', '#283b5b'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BrightColors': {'BACKGROUND': '#b4ffb4', 'TEXT': 'black', 'INPUT': '#ffff64','SCROLL': '#ffb482','TEXT_INPUT' : 'black', 'BUTTON': ('black', '#ffa0dc'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'NeutralBlue': {'BACKGROUND': '#92aa9d', 'TEXT': 'black', 'INPUT': '#fcfff6',
'SCROLL': '#fcfff6', 'TEXT_INPUT': 'black', 'BUTTON': ('black', '#d0dbbd'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'Kayak': {'BACKGROUND': '#a7ad7f', 'TEXT': 'black', 'INPUT': '#e6d3a8',
'SCROLL': '#e6d3a8', 'TEXT_INPUT': 'black', 'BUTTON': ('white', '#5d907d'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'SandyBeach': {'BACKGROUND': '#efeccb', 'TEXT': '#012f2f', 'INPUT': '#e6d3a8',
'SCROLL': '#e6d3a8', 'TEXT_INPUT': '#012f2f', 'BUTTON': ('white', '#046380'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'TealMono': {'BACKGROUND': '#a8cfdd', 'TEXT': 'black', 'INPUT': '#dfedf2','SCROLL': '#dfedf2', 'TEXT_INPUT' : 'black', 'BUTTON': ('white', '#183440'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0}
}
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=colors['BORDER'],
slider_border_width=colors['SLIDER_DEPTH'],
progress_meter_border_depth=colors['PROGRESS_DEPTH'],
scrollbar_color=(colors['SCROLL']),
element_text_color=colors['TEXT'],
input_text_color=colors['TEXT_INPUT'])
except: # most likely an index out of range
pass
# ============================== sprint ======#
# Is identical to the Scrolled Text Box #
# Provides a crude 'print' mechanism but in a #
# GUI environment #
# ============================================#
sprint=ScrolledTextBox
# Converts an object's contents into a nice printable string. Great for dumping debug data
def ObjToStringSingleObj(obj):
if obj is None:
return 'None'
return str(obj.__class__) + '\n' + '\n'.join(
(repr(item) + ' = ' + repr(obj.__dict__[item]) for item in sorted(obj.__dict__)))
def ObjToString(obj, extra=' '):
if obj is None:
return 'None'
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])))
for item in sorted(obj.__dict__)))
# ------------------------------------------------------------------------------------------------------------------ #
# ===================================== Upper PySimpleGUI ======================================================== #
# Pre-built dialog boxes for all your needs These are the "high level API calls #
# ------------------------------------------------------------------------------------------------------------------ #
# ----------------------------------- The mighty Popup! ------------------------------------------------------------ #
def Popup(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'button_type' in _3to2kwargs: button_type = _3to2kwargs['button_type']; del _3to2kwargs['button_type']
else: button_type = POPUP_BUTTONS_OK
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
"""
Popup - Display a popup box with as many parms as you wish to include
:param args:
:param button_color:
:param background_color:
:param text_color:
:param button_type:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
if not args:
args_to_print = ['']
else:
args_to_print = args
if line_width != None:
local_line_width = line_width
else:
local_line_width = MESSAGE_BOX_LINE_WIDTH
title = args_to_print[0] if args_to_print[0] is not None else 'None'
form = Window(title, auto_size_text=True, background_color=background_color, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, icon=icon, font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
max_line_total, total_lines = 0,0
for message in args_to_print:
# fancy code to check if string and convert if not is not need. Just always convert to string :-)
# if not isinstance(message, str): message = str(message)
message = str(message)
if message.count('\n'):
message_wrapped = message
else:
message_wrapped = textwrap.fill(message, local_line_width)
message_wrapped_lines = message_wrapped.count('\n')+1
longest_line_len = max([len(l) for l in message.split('\n')])
width_used = min(longest_line_len, local_line_width)
max_line_total = max(max_line_total, width_used)
# height = _GetNumLinesNeeded(message, width_used)
height = message_wrapped_lines
form.AddRow(Text(message_wrapped, auto_size_text=True, text_color=text_color, background_color=background_color))
total_lines += height
pad = max_line_total-15 if max_line_total > 15 else 1
pad =1
if non_blocking:
PopupButton = DummyButton
else:
PopupButton = SimpleButton
# show either an OK or Yes/No depending on paramater
if button_type is POPUP_BUTTONS_YES_NO:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False, text_color=text_color, background_color=background_color), PopupButton('Yes', button_color=button_color, focus=True, bind_return_key=True), PopupButton('No', button_color=button_color))
elif button_type is POPUP_BUTTONS_CANCELLED:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False, text_color=text_color, background_color=background_color), PopupButton('Cancelled', button_color=button_color, focus=True, bind_return_key=True))
elif button_type is POPUP_BUTTONS_ERROR:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False, text_color=text_color, background_color=background_color), PopupButton('Error', size=(6, 1), button_color=button_color, focus=True, bind_return_key=True))
elif button_type is POPUP_BUTTONS_OK_CANCEL:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False, text_color=text_color, background_color=background_color), PopupButton('OK', size=(5, 1), button_color=button_color, focus=True, bind_return_key=True),
PopupButton('Cancel', size=(5, 1), button_color=button_color))
elif button_type is POPUP_BUTTONS_NO_BUTTONS:
pass
else:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False, background_color=background_color), PopupButton('OK', size=(5, 1), button_color=button_color, focus=True, bind_return_key=True))
if non_blocking:
button, values = form.ReadNonBlocking()
else:
button, values = form.Show()
return button
# ============================== MsgBox============#
# Lazy function. Same as calling Popup with parms #
# This function WILL Disappear perhaps today #
# ==================================================#
# MsgBox is the legacy call and should not be used any longer
def MsgBox(*args):
raise DeprecationWarning('MsgBox is no longer supported... change your call to Popup')
# --------------------------- PopupNoButtons ---------------------------
def PopupNoButtons(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
"""
Show a Popup but without any buttons
:param args:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_color=button_color, background_color=background_color, text_color=text_color, button_type=POPUP_BUTTONS_NO_BUTTONS,
auto_close=auto_close, auto_close_duration=auto_close_duration, non_blocking=non_blocking, icon=icon, line_width=line_width,
font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupNonBlocking ---------------------------
def PopupNonBlocking(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = True
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'button_type' in _3to2kwargs: button_type = _3to2kwargs['button_type']; del _3to2kwargs['button_type']
else: button_type = POPUP_BUTTONS_OK
"""
Show Popup box and immediately return (does not block)
:param args:
:param button_type:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_color=button_color, background_color=background_color, text_color=text_color, button_type=button_type,
auto_close=auto_close, auto_close_duration=auto_close_duration, non_blocking=non_blocking, icon=icon, line_width=line_width,
font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
PopupNoWait = PopupNonBlocking
# --------------------------- PopupNoTitlebar ---------------------------
def PopupNoTitlebar(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = True
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'button_type' in _3to2kwargs: button_type = _3to2kwargs['button_type']; del _3to2kwargs['button_type']
else: button_type = POPUP_BUTTONS_OK
"""
Display a Popup without a titlebar. Enables grab anywhere so you can move it
:param args:
:param button_type:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_color=button_color, background_color=background_color, text_color=text_color, button_type=button_type,
auto_close=auto_close, auto_close_duration=auto_close_duration, non_blocking=non_blocking, icon=icon, line_width=line_width,
font=font, no_titlebar=True, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
PopupNoFrame = PopupNoTitlebar
PopupNoBorder = PopupNoTitlebar
PopupAnnoying = PopupNoTitlebar
# --------------------------- PopupAutoClose ---------------------------
def PopupAutoClose(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = True
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'button_type' in _3to2kwargs: button_type = _3to2kwargs['button_type']; del _3to2kwargs['button_type']
else: button_type = POPUP_BUTTONS_OK
"""
Popup that closes itself after some time period
:param args:
:param button_type:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_color=button_color, background_color=background_color, text_color=text_color, button_type=button_type,
auto_close=auto_close, auto_close_duration=auto_close_duration, non_blocking=non_blocking, icon=icon, line_width=line_width,
font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
PopupTimed = PopupAutoClose
# --------------------------- PopupError ---------------------------
def PopupError(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = DEFAULT_ERROR_BUTTON_COLOR
"""
Popup with colored button and 'Error' as button text
:param args:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_type=POPUP_BUTTONS_ERROR, background_color=background_color, text_color=text_color, non_blocking=non_blocking, icon=icon, line_width=line_width, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupCancel ---------------------------
def PopupCancel(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
"""
Display Popup with "cancelled" button text
:param args:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_type=POPUP_BUTTONS_CANCELLED, background_color=background_color, text_color=text_color, non_blocking=non_blocking, icon=icon, line_width=line_width, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupOK ---------------------------
def PopupOK(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
"""
Display Popup with OK button only
:param args:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_type=POPUP_BUTTONS_OK, background_color=background_color, text_color=text_color, non_blocking=non_blocking, icon=icon, line_width=line_width, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupOKCancel ---------------------------
def PopupOKCancel(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
"""
Display popup with OK and Cancel buttons
:param args:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return: OK, Cancel or None
"""
return Popup(*args, button_type=POPUP_BUTTONS_OK_CANCEL, background_color=background_color, text_color=text_color, non_blocking=non_blocking, icon=icon, line_width=line_width, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupYesNo ---------------------------
def PopupYesNo(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None,None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = False
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = None
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = False
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
"""
Display Popup with Yes and No buttons
:param args:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return: Yes, No or None
"""
return Popup(*args, button_type=POPUP_BUTTONS_YES_NO, background_color=background_color, text_color=text_color, non_blocking=non_blocking, icon=icon, line_width=line_width, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
def main():
window = Window('Demo window..')
window_rows = [[Text('You are running the PySimpleGUI.py file itself')],
[Text('You should be importing it rather than running it', size=(50,2))],
[Text('Here is your sample input window....')],
[Text('Source Folder', size=(15, 1), justification='right'), InputText('Source', focus=True),FolderBrowse()],
[Text('Destination Folder', size=(15, 1), justification='right'), InputText('Dest'), FolderBrowse()],
[Ok(), Cancel()]]
button, (source, dest) = window.LayoutAndRead(window_rows)
if __name__ == '__main__':
main()
exit(69)