Merge pull request #1076 from PySimpleGUI/Dev-latest

Dev latest
This commit is contained in:
MikeTheWatchGuy 2019-01-13 12:44:54 -05:00 committed by GitHub
commit 6ee550a8a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 257 additions and 146 deletions

View File

@ -17,6 +17,7 @@ else:
import types import types
import datetime import datetime
import time
import textwrap import textwrap
import pickle import pickle
import calendar import calendar
@ -26,7 +27,6 @@ g_time_start = 0
g_time_end = 0 g_time_end = 0
g_time_delta = 0 g_time_delta = 0
import time
def TimerStart(): def TimerStart():
@ -3317,7 +3317,7 @@ class TreeData(object):
def __init__(self): def __init__(self):
self.tree_dict = {} self.tree_dict = {}
self.root_node = self.Node("", "", 'root', []) self.root_node = self.Node("", "", 'root', [], None)
self.tree_dict[""] = self.root_node self.tree_dict[""] = self.root_node
def _AddNode(self, key, node): def _AddNode(self, key, node):
@ -3757,7 +3757,7 @@ class Window:
FillFormWithValues(self, values_dict) FillFormWithValues(self, values_dict)
return self return self
def FindElement(self, key, silent_on_error=None): def FindElement(self, key, silent_on_error=False):
element = _FindElementFromKeyInSubForm(self, key) element = _FindElementFromKeyInSubForm(self, key)
if element is None: if element is None:
if not silent_on_error: if not silent_on_error:
@ -5047,8 +5047,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form:Window):
# print(style_name) # print(style_name)
combostyle = ttk.Style() combostyle = ttk.Style()
unique_field = str(time.time()).replace('.','') + '.TCombobox.field'
# Creates a unique name for each field element(Sure there is a better way to do this) # Creates a unique name for each field element(Sure there is a better way to do this)
unique_field = str(datetime.datetime.today().timestamp()).replace('.','') + '.TCombobox.field' # unique_field = str(datetime.datetime.today().timestamp()).replace('.','') + '.TCombobox.field'
# unique_field = str(randint(1,50000000)) + '.TCombobox.field' # unique_field = str(randint(1,50000000)) + '.TCombobox.field'
# print(unique_field) # print(unique_field)
@ -5726,7 +5727,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form:Window):
except: except:
width = element.DefaultColumnWidth width = element.DefaultColumnWidth
treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor) treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor)
def add_treeview_data(node): def add_treeview_data(node):
# print(f'Inserting {node.key} under parent {node.parent}') # print(f'Inserting {node.key} under parent {node.parent}')
if node.key != '': if node.key != '':
@ -5736,8 +5736,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form:Window):
else: else:
photo = tk.PhotoImage(file=node.icon) photo = tk.PhotoImage(file=node.icon)
node.photo = photo node.photo = photo
# except:
# self.photo = None
treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded, image=node.photo) treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded, image=node.photo)
else: else:
treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded) treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded)

View File

@ -29,15 +29,16 @@ else:
import types import types
import datetime import datetime
import time
import textwrap import textwrap
import pickle import pickle
import calendar import calendar
from random import randint
g_time_start = 0 g_time_start = 0
g_time_end = 0 g_time_end = 0
g_time_delta = 0 g_time_delta = 0
import time
def TimerStart(): def TimerStart():
@ -73,8 +74,9 @@ def TimerStop():
# ----====----====----==== Constants the user CAN safely change ====----====----====----# # ----====----====----==== Constants the user CAN safely change ====----====----====----#
# Base64 encoded GIF file
DEFAULT_BASE64_ICON = b'R0lGODlhIQAgAPcAAAAAADBpmDBqmTFqmjJrmzJsnDNtnTRrmTZtmzZumzRtnTdunDRunTRunjVvnzdwnzhwnjlxnzVwoDZxoTdyojhzozl0ozh0pDp1pjp2pjp2pzx0oj12pD52pTt3qD54pjt4qDx4qDx5qTx5qj16qj57qz57rD58rT98rkB4pkJ7q0J9rEB9rkF+rkB+r0d9qkZ/rEl7o0h8p0x9pk5/p0l+qUB+sEyBrE2Crk2Er0KAsUKAskSCtEeEtUWEtkaGuEiHuEiHukiIu0qKu0mJvEmKvEqLvk2Nv1GErVGFr1SFrVGHslaHsFCItFSIs1COvlaPvFiJsVyRuWCNsWSPsWeQs2SQtGaRtW+Wt2qVuGmZv3GYuHSdv3ievXyfvV2XxGWZwmScx2mfyXafwHikyP7TPP/UO//UPP/UPf/UPv7UP//VQP/WQP/WQf/WQv/XQ//WRP7XSf/XSv/YRf/YRv/YR//YSP/YSf/YSv/ZS//aSv/aS/7YTv/aTP/aTf/bTv/bT//cT/7aUf/cUP/cUf/cUv/cU//dVP/dVf7dVv/eVv/eV//eWP/eWf/fWv/fW/7cX/7cYf7cZP7eZf7dav7eb//gW//gXP/gXf/gXv/gX//gYP/hYf/hYv/iYf/iYv7iZP7iZf/iZv/kZv7iaP/kaP/ka//ma//lbP/lbv/mbP/mbv7hdP7lcP/ncP/nc//ndv7gef7gev7iff7ke/7kfv7lf//ocf/ocv/odP/odv/peP/pe//ofIClw4Ory4GszoSszIqqxI+vyoSv0JGvx5OxyZSxyZSzzJi0y5m2zpC10pi715++16C6z6a/05/A2qHC3aXB2K3I3bLH2brP4P7jgv7jh/7mgf7lhP7mhf7liv/qgP7qh/7qiP7rjf7sjP7nkv7nlv7nmP7pkP7qkP7rkv7rlv7slP7sl/7qmv7rnv7snv7sn/7un/7sqv7vq/7vrf7wpv7wqf7wrv7wsv7wtv7ytv7zvP7zv8LU48LV5c3a5f70wP7z0AAAACH5BAEAAP8ALAAAAAAhACAAAAj/AP8JHEiwoMGDCA1uoYIF4bhK1vwlPOjlQICLApwVpFTGzBk1siYSrCLgoskFyQZKMsOypRyR/GKYnBkgQbF/s8603KnmWkIaNIMaw6lzZ8tYB2cIWMo0KIJj/7YV9XgGDRo14gpOIUBggNevXpkKGCDsXySradSoZcMmDsFnDxpEKEC3bl2uXCFQ+7emjV83bt7AgTNroJINAq0wWBxBgYHHdgt0+cdnMJw5c+jQqYNnoARkAx04kPEvS4PTqBswuPIPUp06duzcuYMHT55wAjkwEahsQgqBNSQIHy582D9BePTs2dOnjx8/f1gJ9GXhRpTqApFQoDChu3cOAps///9D/g+gQvYGjrlw4cU/fUnYX6hAn34HgZMABQo0iJB/Qoe8UxAXOQiEg3wIXvCBQLUU4mAhh0R4SCLqJOSEBhhqkAEGHIYgUDaGICIiIoossogj6yBUTQ4htNgiCCB4oIJAtJTIyI2MOOLIIxMtQQIJIwQZpAgwCKRNI43o6Igll1ySSTsI7dOECSaUYOWVKwhkiyVMYuJlJpp0IpA6oJRTkBQopHnCmmu2IBA2mmQi5yZ0fgJKPP+0IwoooZwzkDQ2uCCoCywUyoIW/5DDyaKefOLoJ6LU8w87pJgDTzqmDNSMDpzqYMOnn/7yTyiglBqKKKOMUopA7JgCy0DdeMEjUDM71GqrrcH8QwqqqpbiayqToqJKLwN5g45A0/TAw7LL2krGP634aoopp5yiiiqrZLuKK+jg444uBIHhw7g+MMsDFP/k4wq22rririu4xItLLriAUxAQ5ObrwzL/0PPKu7fIK3C8uxz0w8EIIwzMP/cM7HC88hxEzBBCBGGxxT8AwQzDujws7zcJQVMEEUKUbPITAt1D78OSivSFEUXEXATKA+HTscC80CPSQNGEccQRYhjUDzfxcjPPzkgnLVBAADs='
DEFAULT_BASE64_ICON = b'iVBORw0KGgoAAAANSUhEUgAAACEAAAAgCAMAAACrZuH4AAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAAMGmYMGqZMWqaMmubMmycM22dNGuZNm2bNm6bNG2dN26cNG6dNG6eNW+fN3CfOHCeOXGfNXCgNnGhN3KiOHOjOXSjOHSkOnWmOnamOnanPHSiPXakPnalO3eoPnimO3ioPHioPHmpPHmqPXqqPnurPnusPnytP3yuQHimQnurQn2sQH2uQX6uQH6vR32qRn+sSXujSHynTH2mTn+nSX6pQH6wTIGsTYKuTYSvQoCxQoCyRIK0R4S1RYS2Roa4SIe4SIe6SIi7Soq7SYm8SYq8Sou+TY2/UYStUYWvVIWtUYeyVoewUIi0VIizUI6+Vo+8WImxXJG5YI2xZI+xZ5CzZJC0ZpG1b5a3apW4aZm/cZi4dJ2/eJ69fJ+9XZfEZZnCZJzHaZ/Jdp/AeKTI/tM8/9Q7/9Q8/9Q9/9Q+/tQ//9VA/9ZA/9ZB/9ZC/9dD/9ZE/tdJ/9dK/9hF/9hG/9hH/9hI/9hJ/9hK/9lL/9pK/9pL/thO/9pM/9pN/9tO/9tP/9xP/tpR/9xQ/9xR/9xS/9xT/91U/91V/t1W/95W/95X/95Y/95Z/99a/99b/txf/txh/txk/t5l/t1q/t5v/+Bb/+Bc/+Bd/+Be/+Bf/+Bg/+Fh/+Fi/+Jh/+Ji/uJk/uJl/+Jm/+Rm/uJo/+Ro/+Rr/+Zr/+Vs/+Vu/+Zs/+Zu/uF0/uVw/+dw/+dz/+d2/uB5/uB6/uJ9/uR7/uR+/uV//+hx/+hy/+h0/+h2/+l4/+l7/+h8gKXDg6vLgazOhKzMiqrEj6/KhK/Qka/Hk7HJlLHJlLPMmLTLmbbOkLXSmLvXn77XoLrPpr/Tn8DaocLdpcHYrcjdssfZus/g/uOC/uOH/uaB/uWE/uaF/uWK/+qA/uqH/uqI/uuN/uyM/ueS/ueW/ueY/umQ/uqQ/uuS/uuW/uyU/uyX/uqa/uue/uye/uyf/u6f/uyq/u+r/u+t/vCm/vCp/vCu/vCy/vC2/vK2/vO8/vO/wtTjwtXlzdrl/vTA/vPQAAAAiNpY5gAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAAFw8AABcPASe7rwsAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAAKUSURBVDhPhdB3WE1xHMdxt5JV0dANoUiyd8kqkey996xclUuTlEKidO3qVnTbhIyMW/bee5NskjJLmR/f3++cK/94vP76Ps/n/Zx7z6mE/6koJowcK154vvHOL/GsKCZXkUgkWlf4vWGWq5tsDz+JWIzSokAiqXGe7nWu3HxhEYof7fhOqp1GtptQuMruVhQdxZ05U5G47tYUHbQ4oah6Fg9Z4ubm7i57JhQjdHS0RSzUPoG17u6zZTKZh8c8XlytqW9YWUOH1LqFOZ6enl5ec+XybFb0rweM1tPTM6yuq6vLs0lYJJfLvb19fHwDWGF0jh5lYNAe4/QFemOwxtfXz8/fPyBgwVMqzAcCF4ybAZ2MRCexJGBhYGBQUHDw4u1UHDG1G2ZqB/Q1MTHmzAE+kpCwL1RghlTaBt/6SaXS2kx9YH1IaOjSZST8vfA9JtoDnSngGgL7wkg4WVkofA9mcF1Sx8zMzBK4v3wFiYiMVLxlEy9u21syFhYNmgN7IyJXEYViNZvEYoCVVWOmUVvgQVSUQqGIjolRFvOAFd8HWVs34VoA+6OjY2JjY5Vxm4BC1UuhGG5jY9OUaQXci1MqlfHx8YmqjyhOViW9ZsUN29akJRmPFwkJCZsTSXIpilJffXiTzorLXYgtcxRJKpUqKTklJQ0oSt9FP/EonxVdNY4jla1kK4q2ZB6mIr+AipvduzFUzMSOtLT09IyMzMxtJKug/F0u/6dTexAWDcXXLGEjapKjfsILOLKEuYiSnTQeYCt3UHhbwEHjGMrETfBJU5zq5dSTcXC8hLJccSWP2cgLXHPu7cQNAcpyxF1dyjehAKb0cSYUAOXCUw6V8OFPgevTXFymC+fPPLU677Nw/1X8A/AbfAKGulaqFlIAAAAASUVORK5CYII='
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
DEFAULT_WINDOW_ICON = DEFAULT_BASE64_ICON DEFAULT_WINDOW_ICON = DEFAULT_BASE64_ICON
else: else:
@ -369,7 +371,7 @@ class Element(object):
self.TKEntry = None self.TKEntry = None
self.TKImage = None self.TKImage = None
self.ParentForm = None self.ParentForm = None # type: Window
self.ParentContainer = None # will be a Form, Column, or Frame element self.ParentContainer = None # will be a Form, Column, or Frame element
self.TextInputDefault = None self.TextInputDefault = None
self.Position = (0, 0) # Default position Row 0, Col 0 self.Position = (0, 0) # Default position Row 0, Col 0
@ -379,7 +381,7 @@ class Element(object):
self.Tooltip = tooltip self.Tooltip = tooltip
self.TooltipObject = None self.TooltipObject = None
self.Visible = visible self.Visible = visible
self.TKRightClickMenu = None
def RightClickMenuCallback(self, event): def RightClickMenuCallback(self, event):
self.TKRightClickMenu.tk_popup(event.x_root, event.y_root, 0) self.TKRightClickMenu.tk_popup(event.x_root, event.y_root, 0)
@ -1414,9 +1416,9 @@ class Output(Element):
self._TKOut.output.delete('1.0', tk.END) self._TKOut.output.delete('1.0', tk.END)
self._TKOut.output.insert(tk.END, value) self._TKOut.output.insert(tk.END, value)
if visible is False: if visible is False:
self._TKOut.pack_forget() self._TKOut.frame.pack_forget()
elif visible is True: elif visible is True:
self._TKOut.pack() self._TKOut.frame.pack()
def __del__(self): def __del__(self):
try: try:
@ -1649,10 +1651,15 @@ class Button(Element):
self.TKButton.image = image self.TKButton.image = image
if image_filename is not None: if image_filename is not None:
self.TKButton.config(highlightthickness=0) self.TKButton.config(highlightthickness=0)
photo = tk.PhotoImage(file=image_filename) image = tk.PhotoImage(file=image_filename)
width, height = photo.width(), photo.height() if image_size is not None:
self.TKButton.config(image=photo, width=width, height=height) width, height = image_size
self.TKButton.image = photo else:
width, height = image.width(), image.height()
if image_subsample:
image = image.subsample(image_subsample)
self.TKButton.config(image=image, width=width, height=height)
self.TKButton.image = image
if visible is False: if visible is False:
self.TKButton.pack_forget() self.TKButton.pack_forget()
elif visible is True: elif visible is True:
@ -1721,6 +1728,7 @@ class ButtonMenu(Element):
self.MenuItemChosen = None self.MenuItemChosen = None
self.Tearoff = tearoff self.Tearoff = tearoff
self.TKButtonMenu = None self.TKButtonMenu = None
self.TKMenu = None
# self.temp_size = size if size != (NONE, NONE) else # self.temp_size = size if size != (NONE, NONE) else
super().__init__(ELEM_TYPE_BUTTONMENU, size=size, font=font, pad=pad, key=key, tooltip=tooltip, text_color=self.TextColor, background_color=self.BackgroundColor, visible=visible) super().__init__(ELEM_TYPE_BUTTONMENU, size=size, font=font, pad=pad, key=key, tooltip=tooltip, text_color=self.TextColor, background_color=self.BackgroundColor, visible=visible)
@ -1741,32 +1749,11 @@ class ButtonMenu(Element):
if menu_definition is not None: if menu_definition is not None:
self.TKMenu = tk.Menu(self.TKButtonMenu, tearoff=self.Tearoff) # create the menubar self.TKMenu = tk.Menu(self.TKButtonMenu, tearoff=self.Tearoff) # create the menubar
AddMenuItem(self.TKMenu, menu_definition[1], self) AddMenuItem(self.TKMenu, menu_definition[1], self)
# for menu_entry in menu_definition:
# # print(f'Adding a Menubar ENTRY {menu_entry}')
# baritem = tk.Menu(menubar, tearoff=self.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:]
# if menu_entry[0][0] == MENU_DISABLED_CHARACTER:
# menubar.add_cascade(label=menu_entry[0][len(MENU_DISABLED_CHARACTER):], menu=baritem, underline=pos)
# menubar.entryconfig(menu_entry[0][len(MENU_DISABLED_CHARACTER):], state='disabled')
# else:
# menubar.add_cascade(label=menu_entry[0], menu=baritem, underline=pos)
#
# if len(menu_entry) > 1:
# AddMenuItem(baritem, menu_entry[1], self)
self.TKButtonMenu.configure(menu=self.TKMenu) self.TKButtonMenu.configure(menu=self.TKMenu)
if visible is False:
def UpdateQt(self, menu_definition=None, text=None, button_color=(None, None), font=None, visible=None): self.TKButtonMenu.pack_forget()
if menu_definition is not None: elif visible is True:
menu_def = menu_definition self.TKButtonMenu.pack()
qmenu = QMenu(self.QT_QPushButton)
qmenu.setTitle(menu_def[0])
AddMenuItem(qmenu, menu_def[1], self)
self.QT_QPushButton.setMenu(qmenu)
super().Update(self.QT_QPushButton, background_color=button_color[1], text_color=button_color[0], font=font, visible=visible)
@ -2548,6 +2535,30 @@ class Slider(Element):
super().__del__() super().__del__()
# ---------------------------------------------------------------------- #
# TkScrollableFrame (Used by Column) #
# ---------------------------------------------------------------------- #
class TkFixedFrame(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.canvas = tk.Canvas(self)
self.canvas.pack(side="left", fill="both", expand=True)
# 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)
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
# TkScrollableFrame (Used by Column) # # TkScrollableFrame (Used by Column) #
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
@ -3048,7 +3059,7 @@ MenuBar = Menu # another name for Menu to make it clear it's the Menu B
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Table(Element): class Table(Element):
def __init__(self, values, headings=None, visible_column_map=None, col_widths=None, def_col_width=10, 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, num_rows=None, row_height=None, font=None, justification='right', text_color=None, background_color=None, alternating_row_color=None, auto_size_columns=True, max_col_width=20, select_mode=None, display_row_numbers=False, num_rows=None, row_height=None, font=None, justification='right', text_color=None, background_color=None, alternating_row_color=None, row_colors=None, vertical_scroll_only=True,
size=(None, None), change_submits=False, enable_events=False, bind_return_key=False, pad=None, key=None, tooltip=None, right_click_menu=None, visible=True): size=(None, None), change_submits=False, enable_events=False, bind_return_key=False, pad=None, key=None, tooltip=None, right_click_menu=None, visible=True):
''' '''
Table Table
@ -3095,12 +3106,14 @@ class Table(Element):
self.RowHeight = row_height self.RowHeight = row_height
self.TKTreeview = None self.TKTreeview = None
self.AlternatingRowColor = alternating_row_color self.AlternatingRowColor = alternating_row_color
self.VerticalScrollOnly = vertical_scroll_only
self.SelectedRows = [] self.SelectedRows = []
self.ChangeSubmits = change_submits or enable_events self.ChangeSubmits = change_submits or enable_events
self.BindReturnKey = bind_return_key self.BindReturnKey = bind_return_key
self.StartingRowNumber = 0 # When displaying row numbers, where to start self.StartingRowNumber = 0 # When displaying row numbers, where to start
self.RowHeaderText = 'Row' self.RowHeaderText = 'Row'
self.RightClickMenu = right_click_menu self.RightClickMenu = right_click_menu
self.RowColors = row_colors
super().__init__(ELEM_TYPE_TABLE, text_color=text_color, background_color=background_color, font=font, super().__init__(ELEM_TYPE_TABLE, text_color=text_color, background_color=background_color, font=font,
size=size, pad=pad, key=key, tooltip=tooltip, visible=visible) size=size, pad=pad, key=key, tooltip=tooltip, visible=visible)
@ -3167,7 +3180,7 @@ class Table(Element):
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Tree(Element): class Tree(Element):
def __init__(self, data=None, headings=None, visible_column_map=None, col_widths=None, col0_width=10, def __init__(self, data=None, headings=None, visible_column_map=None, col_widths=None, col0_width=10,
def_col_width=10, auto_size_columns=True, max_col_width=20, select_mode=None, show_expanded=False, change_submits=False, enable_events=False, font=None, justification='right', text_color=None, background_color=None, num_rows=None, pad=None, key=None, tooltip=None,right_click_menu=None, visible=True): def_col_width=10, auto_size_columns=True, max_col_width=20, select_mode=None, show_expanded=False, change_submits=False, enable_events=False, font=None, justification='right', text_color=None, background_color=None, num_rows=None, row_height=None, pad=None, key=None, tooltip=None,right_click_menu=None, visible=True):
''' '''
Tree Tree
:param data: :param data:
@ -3212,6 +3225,9 @@ class Tree(Element):
self.SelectedRows = [] self.SelectedRows = []
self.ChangeSubmits = change_submits or enable_events self.ChangeSubmits = change_submits or enable_events
self.RightClickMenu = right_click_menu self.RightClickMenu = right_click_menu
self.RowHeight = row_height
self.IconList = {}
super().__init__(ELEM_TYPE_TREE, text_color=text_color, background_color=background_color, font=font, pad=pad, super().__init__(ELEM_TYPE_TREE, text_color=text_color, background_color=background_color, font=font, pad=pad,
key=key, tooltip=tooltip, visible=visible) key=key, tooltip=tooltip, visible=visible)
@ -3234,13 +3250,26 @@ class Tree(Element):
def add_treeview_data(self, node): def add_treeview_data(self, node):
# print(f'Inserting {node.key} under parent {node.parent}') # print(f'Inserting {node.key} under parent {node.parent}')
if node.key != '': if node.key != '':
self.TKTreeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, if node.icon:
open=self.ShowExpanded) try:
if type(node.icon) is bytes:
photo = tk.PhotoImage(data=node.icon)
else:
photo = tk.PhotoImage(file=node.icon)
node.photo = photo
self.TKTreeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values,
open=self.ShowExpanded, image=node.photo)
except:
self.photo = None
else:
self.TKTreeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values,
open=self.ShowExpanded)
for node in node.children: for node in node.children:
self.add_treeview_data(node) self.add_treeview_data(node)
def Update(self, values=None, key=None, value=None, text=None, visible=None): def Update(self, values=None, key=None, value=None, text=None, icon=None, visible=None):
if values is not None: if values is not None:
children = self.TKTreeview.get_children() children = self.TKTreeview.get_children()
for i in children: for i in children:
@ -3256,6 +3285,16 @@ class Tree(Element):
self.TKTreeview.item(key, values=value) self.TKTreeview.item(key, values=value)
if text is not None: if text is not None:
self.TKTreeview.item(key, text=text) self.TKTreeview.item(key, text=text)
if icon is not None:
try:
if type(icon) is bytes:
photo = tk.PhotoImage(data=icon)
else:
photo = tk.PhotoImage(file=icon)
self.TKTreeview.item(key, image=photo)
self.IconList[key] = photo # save so that it's not deleted (save reference)
except:
pass
item = self.TKTreeview.item(key) item = self.TKTreeview.item(key)
if visible is False: if visible is False:
self.TKTreeview.pack_forget() self.TKTreeview.pack_forget()
@ -3269,26 +3308,27 @@ class Tree(Element):
class TreeData(object): class TreeData(object):
class Node(object): class Node(object):
def __init__(self, parent, key, text, values): def __init__(self, parent, key, text, values, icon=None):
self.parent = parent self.parent = parent
self.children = [] self.children = []
self.key = key self.key = key
self.text = text self.text = text
self.values = values self.values = values
self.icon = icon
def _Add(self, node): def _Add(self, node):
self.children.append(node) self.children.append(node)
def __init__(self): def __init__(self):
self.tree_dict = {} self.tree_dict = {}
self.root_node = self.Node("", "", 'root', []) self.root_node = self.Node("", "", 'root', [], None)
self.tree_dict[""] = self.root_node self.tree_dict[""] = self.root_node
def _AddNode(self, key, node): def _AddNode(self, key, node):
self.tree_dict[key] = node self.tree_dict[key] = node
def Insert(self, parent, key, text, values): def Insert(self, parent, key, text, values, icon=None):
node = self.Node(parent, key, text, values) node = self.Node(parent, key, text, values, icon)
self.tree_dict[key] = node self.tree_dict[key] = node
parent_node = self.tree_dict[parent] parent_node = self.tree_dict[parent]
parent_node._Add(node) parent_node._Add(node)
@ -3396,7 +3436,7 @@ class Window(object):
self.Font = font if font else DEFAULT_FONT self.Font = font if font else DEFAULT_FONT
self.RadioDict = {} self.RadioDict = {}
self.BorderDepth = border_depth self.BorderDepth = border_depth
self.WindowIcon = icon if icon is not None else Window.user_defined_icon self.WindowIcon = Window.user_defined_icon if Window.user_defined_icon is not None else icon if icon is not None else DEFAULT_WINDOW_ICON
self.AutoClose = auto_close self.AutoClose = auto_close
self.NonBlocking = False self.NonBlocking = False
self.TKroot = None self.TKroot = None
@ -3594,6 +3634,8 @@ class Window(object):
# if the last button clicked was realtime, emulate a read non-blocking # if the last button clicked was realtime, emulate a read non-blocking
# the idea is to quickly return realtime buttons without any blocks until released # the idea is to quickly return realtime buttons without any blocks until released
if self.LastButtonClickedWasRealtime: if self.LastButtonClickedWasRealtime:
self.LastButtonClickedWasRealtime = False # stops from generating events until something changes
# print(f'RTime down {self.LastButtonClicked}' ) # print(f'RTime down {self.LastButtonClicked}' )
try: try:
rc = self.TKroot.update() rc = self.TKroot.update()
@ -3719,15 +3761,18 @@ class Window(object):
FillFormWithValues(self, values_dict) FillFormWithValues(self, values_dict)
return self return self
def FindElement(self, key): def FindElement(self, key, silent_on_error=False):
element = _FindElementFromKeyInSubForm(self, key) element = _FindElementFromKeyInSubForm(self, key)
if element is None: if element is None:
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***') if not silent_on_error:
PopupError('Keyword error in FindElement Call', print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
'Bad key = {}'.format(key), PopupError('Keyword error in FindElement Call',
'Your bad line of code may resemble this:', 'Bad key = {}'.format(key),
'window.FindElement("{}")'.format(key)) 'Your bad line of code may resemble this:',
return ErrorElement(key=key) 'window.FindElement("{}")'.format(key))
return ErrorElement(key=key)
else:
return False
return element return element
Element = FindElement # Shortcut function Element = FindElement # Shortcut function
@ -4340,7 +4385,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form):
value = 0 value = 0
elif element.Type == ELEM_TYPE_INPUT_SLIDER: elif element.Type == ELEM_TYPE_INPUT_SLIDER:
try: try:
value = element.TKIntVar.get() value = float(element.TKScale.get())
except: except:
value = 0 value = 0
elif element.Type == ELEM_TYPE_INPUT_MULTILINE: elif element.Type == ELEM_TYPE_INPUT_MULTILINE:
@ -4687,10 +4732,18 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
highlightthickness=0) highlightthickness=0)
element.TKColFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0) element.TKColFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0)
else: else:
element.TKColFrame = tk.Frame(tk_row_frame)
if element.Size != (None, None): if element.Size != (None, None):
element.TKColFrame.configure(width=element.Size[0], height=element.Size[1]) element.TKColFrame = TkFixedFrame(tk_row_frame)
PackFormIntoFrame(element, element.TKColFrame, toplevel_form) PackFormIntoFrame(element, element.TKColFrame.TKFrame, toplevel_form)
element.TKColFrame.TKFrame.update()
if element.Size[1] is not None:
element.TKColFrame.canvas.config(height=element.Size[1])
elif element.Size[0] is not None:
element.TKColFrame.canvas.config(width=element.Size[0])
else:
element.TKColFrame = tk.Frame(tk_row_frame)
PackFormIntoFrame(element, element.TKColFrame, toplevel_form)
element.TKColFrame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both') element.TKColFrame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both')
if element.Visible is False: if element.Visible is False:
element.TKColFrame.pack_forget() element.TKColFrame.pack_forget()
@ -4946,7 +4999,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
timeout=DEFAULT_TOOLTIP_TIME) timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- INPUT (Single Line) element ------------------------- # # ------------------------- INPUT element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_TEXT: elif element_type == ELEM_TYPE_INPUT_TEXT:
default_text = element.DefaultText default_text = element.DefaultText
element.TKStringVar = tk.StringVar() element.TKStringVar = tk.StringVar()
@ -4983,7 +5036,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
AddMenuItem(top_menu, menu[1], element) AddMenuItem(top_menu, menu[1], element)
element.TKRightClickMenu = top_menu element.TKRightClickMenu = top_menu
element.TKEntry.bind('<Button-3>', element.RightClickMenuCallback) element.TKEntry.bind('<Button-3>', element.RightClickMenuCallback)
# ------------------------- COMBO BOX (Drop Down) element ------------------------- # # ------------------------- COMBOBOX element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_COMBO: elif element_type == ELEM_TYPE_INPUT_COMBO:
max_line_len = max([len(str(l)) for l in element.Values]) max_line_len = max([len(str(l)) for l in element.Values])
if auto_size_text is False: if auto_size_text is False:
@ -4993,66 +5046,49 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKStringVar = tk.StringVar() element.TKStringVar = tk.StringVar()
style_name = 'TCombobox' style_name = 'TCombobox'
if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT: if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
# print('Combo special style', element.TextColor, element.BackgroundColor) # Creates 1 style per Text Color/ Background Color combination
style_name = 'PSG.TCombobox' style_name = element.TextColor + element.BackgroundColor + '.TCombobox'
# print(style_name)
combostyle = tkinter.ttk.Style() combostyle = tkinter.ttk.Style()
#
# combostyle.map("C.TButton",
# foreground=[('pressed', 'red'), ('active', 'blue')], # Creates a unique name for each field element(Sure there is a better way to do this)
# background=[('pressed', '!disabled', 'black'), ('active', 'white')] unique_field = str(time.time()).replace('.','') + '.TCombobox.field'
# ) # unique_field = str(randint(1,50000000)) + '.TCombobox.field'
# combostyle.map('PSG.TCombobox', background=[('selected', 'green')])
# combostyle.configure('PSG.TCombobox.Listbox',foreground='green') # print(unique_field)
# combostyle.map('PSG.TCombobox', foreground=[('active','purple')]) # Clones over the TCombobox.field element from the "alt" theme.
# combostyle.map('PSG.TCombobox.textarea', foreground=[('active','purple')]) # This is what will allow us to change the background color without altering the whole programs theme
# combostyle.map('PSG.TCombobox.rightdownarrow', arrowcolor=[('active','purple')]) combostyle.element_create(unique_field, "from", "alt")
# combostyle.configure('PSG.TCombobox.TEntry', background='red')
# combostyle.configure('PSG.TCombobox', background=element.BackgroundColor) # Create widget layout using cloned "alt" field
combostyle.configure('PSG.TCombobox', foreground=element.TextColor) # WORKS combostyle.layout(style_name, [
combostyle.configure('PSG.TCombobox', selectbackground='gray70') # WORKS (unique_field, {'children': [('Combobox.downarrow', {'side': 'right', 'sticky': 'ns'}),
combostyle.configure('PSG.TCombobox', selectforeground=element.TextColor) # WORKS ('Combobox.padding',
# combostyle.configure('PSG.TCombobox.Listbox', background='purple') {'children': [('Combobox.focus',
# toplevel_form.TKroot.option_add("*TCombobox*Background", element.BackgroundColor) # WORK for drop-down list (Changes all) {'children': [('Combobox.textarea',
# combostyle.map('PSG.TCombobox', background=[('active', 'purple'), ('disabled', 'purple')]) {'sticky': 'nswe'})],
# combostyle.configure('PSG.TCombobox.PopdownFrame', background=element.BackgroundColor) 'expand': '1',
# combostyle.configure('PSG.TCombobox.field', fieldbackground=element.BackgroundColor) 'sticky': 'nswe'})],
# combostyle.configure('PSG.TCombobox.Listbox', background=element.BackgroundColor) 'expand': '1',
# print(combostyle.element_names()) 'sticky': 'nswe'})],
# print(combostyle.element_options('PSG.TCombobox')) 'sticky': 'nswe'})])
# try:
# combostyle.theme_create('combostyle', # Copy default TCombobox settings
# settings={'TCombobox': # Getting an error on this line of code
# {'configure': # combostyle.configure(style_name, *combostyle.configure("TCombobox"))
# {'selectbackground': 'gray50',
# 'fieldbackground': element.BackgroundColor, # Set individual widget options
# 'foreground': text_color, combostyle.configure(style_name, foreground=element.TextColor)
# 'background': element.BackgroundColor} combostyle.configure(style_name, selectbackground='gray70')
# }}) combostyle.configure(style_name, fieldbackground=element.BackgroundColor)
# except: combostyle.configure(style_name, selectforeground=element.TextColor)
# try:
# combostyle.theme_settings('combostyle',
# settings={'TCombobox':
# {'configure':
# {'selectbackground': 'gray50',
# '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, style=style_name) element.TKCombo = tkinter.ttk.Combobox(tk_row_frame, width=width, textvariable=element.TKStringVar, font=font, style=style_name)
if element.Size[1] != 1 and element.Size[1] is not None: if element.Size[1] != 1 and element.Size[1] is not None:
element.TKCombo.configure(height=element.Size[1]) element.TKCombo.configure(height=element.Size[1])
# element.TKCombo['state']='readonly'
element.TKCombo['values'] = element.Values 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=elementpad[0], pady=elementpad[1]) element.TKCombo.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1])
if element.Visible is False: if element.Visible is False:
element.TKCombo.pack_forget() element.TKCombo.pack_forget()
@ -5071,7 +5107,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKCombo['state'] = 'disabled' element.TKCombo['state'] = 'disabled'
if element.Tooltip is not None: if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- # # ------------------------- OPTION MENU Element (Like ComboBox but different) element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_OPTION_MENU: elif element_type == ELEM_TYPE_INPUT_OPTION_MENU:
max_line_len = max([len(str(l)) for l in element.Values]) max_line_len = max([len(str(l)) for l in element.Values])
if auto_size_text is False: if auto_size_text is False:
@ -5170,7 +5206,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
AddMenuItem(top_menu, menu[1], element) AddMenuItem(top_menu, menu[1], element)
element.TKRightClickMenu = top_menu element.TKRightClickMenu = top_menu
element.TKText.bind('<Button-3>', element.RightClickMenuCallback) element.TKText.bind('<Button-3>', element.RightClickMenuCallback)
# ------------------------- INPUT CHECKBOX element ------------------------- # # ------------------------- CHECKBOX element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_CHECKBOX: elif element_type == ELEM_TYPE_INPUT_CHECKBOX:
width = 0 if auto_size_text else element_size[0] width = 0 if auto_size_text else element_size[0]
default_value = element.InitialState default_value = element.InitialState
@ -5217,7 +5253,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKProgressBar.TKProgressBarForReal.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1]) element.TKProgressBar.TKProgressBarForReal.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1])
if element.Visible is False: if element.Visible is False:
element.TKProgressBar.TKProgressBarForReal.pack_forget() element.TKProgressBar.TKProgressBarForReal.pack_forget()
# ------------------------- INPUT RADIO BUTTON element ------------------------- # # ------------------------- RADIO BUTTON element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_RADIO: elif element_type == ELEM_TYPE_INPUT_RADIO:
width = 0 if auto_size_text else element_size[0] width = 0 if auto_size_text else element_size[0]
default_value = element.InitialState default_value = element.InitialState
@ -5251,7 +5287,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKRadio.pack_forget() element.TKRadio.pack_forget()
if element.Tooltip is not None: if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKRadio, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) element.TooltipObject = ToolTip(element.TKRadio, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- INPUT SPIN Box element ------------------------- # # ------------------------- SPIN element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_SPIN: elif element_type == ELEM_TYPE_INPUT_SPIN:
width, height = element_size width, height = element_size
width = 0 if auto_size_text else element_size[0] width = 0 if auto_size_text else element_size[0]
@ -5282,7 +5318,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
pad=elementpad) pad=elementpad)
element._TKOut.pack(side=tk.LEFT, expand=True, fill='both') element._TKOut.pack(side=tk.LEFT, expand=True, fill='both')
if element.Visible is False: if element.Visible is False:
element._TKOut.pack_forget() element._TKOut.frame.pack_forget()
if element.Tooltip is not None: if element.Tooltip is not None:
element.TooltipObject = ToolTip(element._TKOut, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) element.TooltipObject = ToolTip(element._TKOut, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
if element.RightClickMenu or toplevel_form.RightClickMenu: if element.RightClickMenu or toplevel_form.RightClickMenu:
@ -5313,7 +5349,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
else: else:
element.tktext_label = tk.Label(tk_row_frame, width=width, height=height, bd=border_depth) element.tktext_label = tk.Label(tk_row_frame, width=width, height=height, bd=border_depth)
if element.BackgroundColor is not None: if element.BackgroundColor is not None:
element.tktext_label.config(background=element.BackgroundColor); element.tktext_label.config(background=element.BackgroundColor)
element.tktext_label.image = photo element.tktext_label.image = photo
# tktext_label.configure(anchor=tk.NW, image=photo) # tktext_label.configure(anchor=tk.NW, image=photo)
@ -5556,6 +5592,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TooltipObject = ToolTip(element.TKScale, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) element.TooltipObject = ToolTip(element.TKScale, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- TABLE element ------------------------- # # ------------------------- TABLE element ------------------------- #
elif element_type == ELEM_TYPE_TABLE: elif element_type == ELEM_TYPE_TABLE:
element = element # type: Table
frame = tk.Frame(tk_row_frame) frame = tk.Frame(tk_row_frame)
height = element.NumRows height = element.NumRows
@ -5575,7 +5612,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
except: except:
column_widths[i] = col_width column_widths[i] = col_width
if element.ColumnsToDisplay is None: if element.ColumnsToDisplay is None:
displaycolumns = element.ColumnHeadings displaycolumns = element.ColumnHeadings if element.ColumnHeadings is not None else element.Values[0]
else: else:
displaycolumns = [] displaycolumns = []
for i, should_display in enumerate(element.ColumnsToDisplay): for i, should_display in enumerate(element.ColumnsToDisplay):
@ -5587,13 +5624,15 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
column_headings = [element.RowHeaderText, ] + element.ColumnHeadings column_headings = [element.RowHeaderText, ] + element.ColumnHeadings
element.TKTreeview = tkinter.ttk.Treeview(frame, columns=column_headings, element.TKTreeview = tkinter.ttk.Treeview(frame, columns=column_headings,
displaycolumns=displaycolumns, show='headings', height=height, displaycolumns=displaycolumns, show='headings', height=height,
selectmode=element.SelectMode) selectmode=element.SelectMode,)
treeview = element.TKTreeview treeview = element.TKTreeview
if element.DisplayRowNumbers: if element.DisplayRowNumbers:
treeview.heading(element.RowHeaderText, text=element.RowHeaderText) # make a dummy heading treeview.heading(element.RowHeaderText, text=element.RowHeaderText) # make a dummy heading
treeview.column(element.RowHeaderText, width=50, anchor=anchor) treeview.column(element.RowHeaderText, width=50, anchor=anchor)
for i, heading in enumerate(element.ColumnHeadings):
treeview.heading(heading, text=heading) headings = element.ColumnHeadings if element.ColumnHeadings is not None else element.Values[0]
for i, heading in enumerate(headings):
# treeview.heading(heading, text=heading)
if element.AutoSizeColumns: if element.AutoSizeColumns:
width = max(column_widths[i], len(heading)) width = max(column_widths[i], len(heading))
else: else:
@ -5601,15 +5640,23 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
width = element.ColumnWidths[i] width = element.ColumnWidths[i]
except: except:
width = element.DefaultColumnWidth width = element.DefaultColumnWidth
treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor) treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor)
# Insert values into the tree # Insert values into the tree
for i, value in enumerate(element.Values): for i, value in enumerate(element.Values):
if element.DisplayRowNumbers: if element.DisplayRowNumbers:
value = [i+element.StartingRowNumber] + value value = [i+element.StartingRowNumber] + value
id = treeview.insert('', 'end', text=value, iid=i + 1, values=value, tag=i % 2) id = treeview.insert('', 'end', text=value, iid=i + 1, values=value, tag=i)
if element.AlternatingRowColor is not None: if element.AlternatingRowColor is not None: # alternating colors
treeview.tag_configure(1, background=element.AlternatingRowColor) for row in range(0, len(element.Values), 2):
treeview.tag_configure(row, background=element.AlternatingRowColor)
if element.RowColors is not None: # individual row colors
for row_def in element.RowColors:
if len(row_def) == 2: # only background is specified
treeview.tag_configure(row_def[0], background=row_def[1])
else:
treeview.tag_configure(row_def[0], background=row_def[2], foreground=row_def[1])
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
tkinter.ttk.Style().configure("Treeview", background=element.BackgroundColor, tkinter.ttk.Style().configure("Treeview", background=element.BackgroundColor,
fieldbackground=element.BackgroundColor) fieldbackground=element.BackgroundColor)
@ -5622,9 +5669,17 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
if element.BindReturnKey: if element.BindReturnKey:
treeview.bind('<Return>', element.treeview_double_click) treeview.bind('<Return>', element.treeview_double_click)
treeview.bind('<Double-Button-1>', element.treeview_double_click) treeview.bind('<Double-Button-1>', element.treeview_double_click)
scrollbar = tk.Scrollbar(frame) scrollbar = tk.Scrollbar(frame)
scrollbar.pack(side=tk.RIGHT, fill='y') scrollbar.pack(side=tk.RIGHT, fill='y')
scrollbar.config(command=treeview.yview) scrollbar.config(command=treeview.yview)
if not element.VerticalScrollOnly:
hscrollbar = tk.Scrollbar(frame, orient=tk.HORIZONTAL)
hscrollbar.pack(side=tk.BOTTOM, fill='x')
hscrollbar.config(command=treeview.xview)
treeview.configure(xscrollcommand=hscrollbar.set)
treeview.configure(yscrollcommand=scrollbar.set) treeview.configure(yscrollcommand=scrollbar.set)
element.TKTreeview.pack(side=tk.LEFT, expand=True, padx=0, pady=0, fill='both') element.TKTreeview.pack(side=tk.LEFT, expand=True, padx=0, pady=0, fill='both')
@ -5642,6 +5697,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKTreeview.bind('<Button-3>', element.RightClickMenuCallback) element.TKTreeview.bind('<Button-3>', element.RightClickMenuCallback)
# ------------------------- Tree element ------------------------- # # ------------------------- Tree element ------------------------- #
elif element_type == ELEM_TYPE_TREE: elif element_type == ELEM_TYPE_TREE:
element = element #type: Tree
frame = tk.Frame(tk_row_frame) frame = tk.Frame(tk_row_frame)
height = element.NumRows height = element.NumRows
@ -5663,7 +5719,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
# ------------- GET THE TREEVIEW WIDGET ------------- # ------------- GET THE TREEVIEW WIDGET -------------
element.TKTreeview = tkinter.ttk.Treeview(frame, columns=column_headings, element.TKTreeview = tkinter.ttk.Treeview(frame, columns=column_headings,
displaycolumns=displaycolumns, show='tree headings', height=height, displaycolumns=displaycolumns, show='tree headings', height=height,
selectmode=element.SelectMode, ) selectmode=element.SelectMode)
treeview = element.TKTreeview treeview = element.TKTreeview
for i, heading in enumerate(element.ColumnHeadings): # Configure cols + headings for i, heading in enumerate(element.ColumnHeadings): # Configure cols + headings
treeview.heading(heading, text=heading) treeview.heading(heading, text=heading)
@ -5675,11 +5731,19 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
except: except:
width = element.DefaultColumnWidth width = element.DefaultColumnWidth
treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor) treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor)
def add_treeview_data(node): def add_treeview_data(node):
# print(f'Inserting {node.key} under parent {node.parent}') # print(f'Inserting {node.key} under parent {node.parent}')
if node.key != '': if node.key != '':
treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded) if node.icon:
if type(node.icon) is bytes:
photo = tk.PhotoImage(data=node.icon)
else:
photo = tk.PhotoImage(file=node.icon)
node.photo = photo
treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded, image=node.photo)
else:
treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded)
for node in node.children: for node in node.children:
add_treeview_data(node) add_treeview_data(node)
@ -5692,6 +5756,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT: if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
tkinter.ttk.Style().configure("Treeview", foreground=element.TextColor) tkinter.ttk.Style().configure("Treeview", foreground=element.TextColor)
tkinter.ttk.Style().configure("Treeview", font=font)
if element.RowHeight:
tkinter.ttk.Style().configure("Treeview", rowheight=element.RowHeight)
scrollbar = tk.Scrollbar(frame) scrollbar = tk.Scrollbar(frame)
scrollbar.pack(side=tk.RIGHT, fill='y') scrollbar.pack(side=tk.RIGHT, fill='y')
scrollbar.config(command=treeview.yview) scrollbar.config(command=treeview.yview)
@ -5712,6 +5779,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKTreeview.bind('<Button-3>', element.RightClickMenuCallback) element.TKTreeview.bind('<Button-3>', element.RightClickMenuCallback)
# ------------------------- Separator element ------------------------- # # ------------------------- Separator element ------------------------- #
elif element_type == ELEM_TYPE_SEPARATOR: elif element_type == ELEM_TYPE_SEPARATOR:
element = element # type: VerticalSeparator
separator = tkinter.ttk.Separator(tk_row_frame, orient=element.Orientation, ) separator = tkinter.ttk.Separator(tk_row_frame, orient=element.Orientation, )
separator.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill='both', expand=True) separator.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], fill='both', expand=True)
# ------------------------- StatusBar element ------------------------- # # ------------------------- StatusBar element ------------------------- #
@ -5819,7 +5887,6 @@ def ConvertFlexToTK(MyFlexForm):
# ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----# # ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----#
def StartupTK(my_flex_form): def StartupTK(my_flex_form):
# global _my_windows # global _my_windows
# ow = _my_windows.NumOpenWindows # ow = _my_windows.NumOpenWindows
ow = Window.NumOpenWindows ow = Window.NumOpenWindows
# print('Starting TK open Windows = {}'.format(ow)) # print('Starting TK open Windows = {}'.format(ow))
@ -7734,7 +7801,7 @@ def main():
frame2 = [ frame2 = [
[Listbox(['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(20, 5))], [Listbox(['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(20, 5))],
[Combo(['Combo item 1', ], size=(20, 3))], [Combo(['Combo item 1', ], size=(20, 3), text_color='red', background_color='red')],
[Spin([1, 2, 3], size=(4, 3))], [Spin([1, 2, 3], size=(4, 3))],
] ]

View File

@ -31,9 +31,9 @@
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.21.0-red.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.22.0-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.21.0-blue.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.22.0-blue.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.22.0-orange.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.22.0-orange.svg?longCache=true&style=for-the-badge)
@ -1507,7 +1507,7 @@ There are a few methods (functions) that you will see in this document that act
window.Close() - To close your window, if a button hasn't already closed it window.Close() - To close your window, if a button hasn't already closed it
window.Disable() - Use to disable the window inputwhen opening another window on top of the primnary Window window.Disable() - Use to disable the window inputwhen opening another window on top of the primnary Window
window.Enable() - Re-enable a Disabled window window.Enable() - Re-enable a Disabled window
window.FindElement(key) - Returns the element that has a matching key value window.FindElement(key, silent_on_error=False) - Returns the element that has a matching key value
window.Move(x,y) - Moves window to location x,y on screen' window.Move(x,y) - Moves window to location x,y on screen'
window.SetAlpha(alpha) - Changes window transparency window.SetAlpha(alpha) - Changes window transparency
window.BringToFront() - Brings the window to the top of other windows on the screen window.BringToFront() - Brings the window to the top of other windows on the screen
@ -1533,6 +1533,9 @@ window = sg.Window('My window title').Layout(layout)
Call to force a window to go through the final stages of initialization. This will cause the tkinter resources to be allocated so that they can then be modified. This also causes your window to appear. If you do not want your window to appear when Finalize is called, then set the Alpha to 0 in your window's creation parameters. Call to force a window to go through the final stages of initialization. This will cause the tkinter resources to be allocated so that they can then be modified. This also causes your window to appear. If you do not want your window to appear when Finalize is called, then set the Alpha to 0 in your window's creation parameters.
If you want to call an element's Update method or call a Graph element's drawing primitives, you ***must*** either call Read or Finalize prior to making those calls.
#### Read(timeout=None, timeout_key='__TIMEOUT_ _ ') #### Read(timeout=None, timeout_key='__TIMEOUT_ _ ')
Read the Window's input values and button clicks in a blocking-fashion Read the Window's input values and button clicks in a blocking-fashion
@ -3091,6 +3094,8 @@ Table( values,
text_color=None, text_color=None,
background_color=None, background_color=None,
alternating_row_color=None, alternating_row_color=None,
row_colors=None,
vertical_scroll_only=True,
size=(None,None), size=(None,None),
change_submits=False, change_submits=False,
enable_events=False, enable_events=False,
@ -3098,6 +3103,7 @@ Table( values,
pad=None, pad=None,
key=None, key=None,
tooltip=None, tooltip=None,
right_click_menu=None,
visible=True): visible=True):
``` ```
@ -3117,6 +3123,8 @@ font - font for table entries
justification - left, right, center justification - left, right, center
text_color - color of text text_color - color of text
alternating row color - if set will change background color for alternating rows alternating row color - if set will change background color for alternating rows
row_colors - list of tuples representing (row_number, color) e.g. row_colors = ((5, 'white', 'blue'), (0,'red'), (15,'yellow'))
vertical_scroll_only - if True will not show a horizontal scrollbar. NOTE - will have to disable to get horizontal scrollbars
background_color - cell background color background_color - cell background color
size - (None, number of rows) - don't use, use num_rows instead size - (None, number of rows) - don't use, use num_rows instead
enable_events - will return a 'row selected' event when row is selected enable_events - will return a 'row selected' event when row is selected
@ -3147,7 +3155,8 @@ The Tree Element and Table Element are close cousins. Many of the parameters f
Tree( data=None, Tree( data=None,
headings=None, headings=None,
visible_column_map=None, visible_column_map=None,
col_widths=None, col0_width=10, col_widths=None,
col0_width=10,
def_col_width=10, def_col_width=10,
auto_size_columns=True, auto_size_columns=True,
max_col_width=20, max_col_width=20,
@ -3160,6 +3169,7 @@ Tree( data=None,
text_color=None, text_color=None,
background_color=None, background_color=None,
num_rows=None, num_rows=None,
row_height=None,
pad=None, pad=None,
key=None, key=None,
tooltip=None, tooltip=None,
@ -3182,6 +3192,7 @@ class Tree(data=None - data in TreeData format
text_color=None- color of text to display text_color=None- color of text to display
background_color=None - background color background_color=None - background color
num_rows=None - number of rows to display num_rows=None - number of rows to display
row_height=None - height of rows in pixels
pad=None - element padding pad=None - element padding
key=None - key for element key=None - key for element
tooltip=None - tooltip tooltip=None - tooltip
@ -3192,6 +3203,12 @@ Unlike Tables there is no standard format for trees. Thus the data structure pa
* "Insert" data into the tree * "Insert" data into the tree
* Pass the filled in TreeData object to Tree Element * Pass the filled in TreeData object to Tree Element
#### TreeData format
```python
def TreeData()
def Insert(self, parent, key, text, values, icon=None)
```
To "insert" data into the tree the TreeData method Insert is called. To "insert" data into the tree the TreeData method Insert is called.
`Insert(parent_key, key, display_text, values)` `Insert(parent_key, key, display_text, values)`
@ -3211,6 +3228,14 @@ Note that you ***can*** use the same values for display_text and keys. The only
When Reading a window the Table Element will return a list of rows that are selected by the user. The list will be empty is no rows are selected. When Reading a window the Table Element will return a list of rows that are selected by the user. The list will be empty is no rows are selected.
#### Icons on Tree Entries
If you wish to show an icon next to a tree item, then you specify the icon in the call to `Insert`. You pass in a filename or a Base64 bytes string using the optional `icon` parameter.
Here is the result of showing an icon with a tree entry.
![image](https://user-images.githubusercontent.com/13696193/51087270-2b561e80-171f-11e9-8260-6142ea9b1137.png)
## Tab and Tab Group Elements ## Tab and Tab Group Elements
@ -4423,7 +4448,7 @@ Listboxes are still without scrollwheels. The mouse can drag to see more items.
3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned. 3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned.
One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn this off for windows with sliders. This feature is enabled in the Window call. One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn off for windows with sliders. This feature is enabled in the Window call.
Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to Window. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager. Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to Window. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager.
@ -4773,7 +4798,7 @@ Emergency patch release... going out same day as previous release
## 3.20.0 & 1.20.0 18-Dec-2018 ## 3.20.0 & 1.20.0 18-Dec-2018
* New Pane Element * New Pane Element
* Graphh.DeleteFigure method * Graph.DeleteFigure method
* disable_minimize - New parameter for Window * disable_minimize - New parameter for Window
* Fix for 2.7 menus * Fix for 2.7 menus
* Debug Window no longer re-routes stdout by default * Debug Window no longer re-routes stdout by default
@ -4806,6 +4831,24 @@ Emergency patch release... going out same day as previous release
* Attempted to use Styles better with Combobox * Attempted to use Styles better with Combobox
* Fixed bug blocking setting bar colors in OneLineProgressMeter * Fixed bug blocking setting bar colors in OneLineProgressMeter
# 3.22.0 PySimpleGUI / 1.22.0 PySimpleGUI27
* Added type hints to some portions of the code
* Output element can be made invisible
* Image sizing and subsample for Button images
* Invisibility for ButtonMenus
* Attempt at specifying size of Column elements (limited success)
* Table Element
* New row_colors parameter
* New vertical_scroll_only parameter - NOTE - will have to disable to get horizontal scrollbars
* Tree Element
* New row_height parameter
* New feature - Icons for tree entries using filename or Base64 images
* Fix for bug sending back continuous mouse events
* New parameter silence_on_error for FindElement / Element calls
* Slider returns float now
* Fix for Menus when using Python 2.7
* Combobox Styling (again)
### Upcoming ### Upcoming
@ -4914,3 +4957,6 @@ For Python questions, I simply start my query with 'Python'. Let's say you forg
In the hands of a competent programmer, this tool is **amazing**. It's a must-try kind of program that has completely changed my programming process. I'm not afraid of asking for help! You just have to be smart about using what you find. In the hands of a competent programmer, this tool is **amazing**. It's a must-try kind of program that has completely changed my programming process. I'm not afraid of asking for help! You just have to be smart about using what you find.
The PySimpleGUI window that the results are shown in is an 'input' field which means you can copy and paste the results right into your code. The PySimpleGUI window that the results are shown in is an 'input' field which means you can copy and paste the results right into your code.
<!--stackedit_data:
eyJoaXN0b3J5IjpbLTE2NzI2ODY1NTldfQ==
-->