From 879b3244b1ad61bc53b9423705e590c48e264e03 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:21:29 -0500 Subject: [PATCH 01/15] readme.md updated from https://stackedit.io/ --- readme.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 803f6913..bc66dd0c 100644 --- a/readme.md +++ b/readme.md @@ -4806,6 +4806,24 @@ Emergency patch release... going out same day as previous release * Attempted to use Styles better with Combobox * Fixed bug blocking setting bar colors in OneLineProgressMeter +# 3.22.0 PySimpleGUI + +* 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 paramter + * New vertical_scroll_only parameter +* Tree Element + * New row_height paramter + * New feature - Icons for tree entries using filename or Base64 images +* Fix for bug sending back continuous mouse events +* New paramter silence_on_error for FindElement / Element calls +* Slider returns float now +* Fix for Menus when using Python 2.7 +* Combobox Styling (again) ### Upcoming @@ -4913,4 +4931,7 @@ 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. -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. \ No newline at end of file +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. + \ No newline at end of file From 0c108df99c214e838d22109bbe5b6d9932792f47 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:23:08 -0500 Subject: [PATCH 02/15] readme.md updated from https://stackedit.io/ --- readme.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index bc66dd0c..b6d48624 100644 --- a/readme.md +++ b/readme.md @@ -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) @@ -3147,7 +3147,8 @@ The Tree Element and Table Element are close cousins. Many of the parameters f Tree( data=None, headings=None, visible_column_map=None, - col_widths=None, col0_width=10, + col_widths=None, + col0_width=10, def_col_width=10, auto_size_columns=True, max_col_width=20, @@ -3160,6 +3161,7 @@ Tree( data=None, text_color=None, background_color=None, num_rows=None, + row_height=None, pad=None, key=None, tooltip=None, @@ -4933,5 +4935,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 7407413177e25aa72ca82de444406f81b404f165 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:24:45 -0500 Subject: [PATCH 03/15] readme.md updated from https://stackedit.io/ --- readme.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index b6d48624..b462cd22 100644 --- a/readme.md +++ b/readme.md @@ -3091,6 +3091,7 @@ Table( values, text_color=None, background_color=None, alternating_row_color=None, + row_colors=None, size=(None,None), change_submits=False, enable_events=False, @@ -3183,7 +3184,8 @@ class Tree(data=None - data in TreeData format justification='right' - justification for data display text_color=None- color of text to display 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 key=None - key for element tooltip=None - tooltip @@ -4935,5 +4937,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 70ff86b179b2aa69c8ded9aefeb6d0205e24a112 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:26:22 -0500 Subject: [PATCH 04/15] readme.md updated from https://stackedit.io/ --- readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index b462cd22..5d673b7d 100644 --- a/readme.md +++ b/readme.md @@ -3092,6 +3092,7 @@ Table( values, background_color=None, alternating_row_color=None, row_colors=None, + vertical_scroll_only=True, size=(None,None), change_submits=False, enable_events=False, @@ -3099,6 +3100,7 @@ Table( values, pad=None, key=None, tooltip=None, + right_click_menu=None, visible=True): ``` @@ -3118,6 +3120,7 @@ font - font for table entries justification - left, right, center text_color - color of text alternating row color - if set will change background color for alternating rows +row_colors - list of tuples representing (row_number, color) background_color - cell background color size - (None, number of rows) - don't use, use num_rows instead enable_events - will return a 'row selected' event when row is selected @@ -4937,5 +4940,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 3547d139b0422f6e407326084d7a369f2c1d2c4b Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:28:01 -0500 Subject: [PATCH 05/15] readme.md updated from https://stackedit.io/ --- readme.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 5d673b7d..acf00d90 100644 --- a/readme.md +++ b/readme.md @@ -3120,7 +3120,8 @@ font - font for table entries justification - left, right, center text_color - color of text alternating row color - if set will change background color for alternating rows -row_colors - list of tuples representing (row_number, color) +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 background_color - cell background color size - (None, number of rows) - don't use, use num_rows instead enable_events - will return a 'row selected' event when row is selected @@ -4940,5 +4941,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 211f0bb77d3b406247802bf2a61b107ecd917bb6 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:29:39 -0500 Subject: [PATCH 06/15] readme.md updated from https://stackedit.io/ --- readme.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index acf00d90..f02db25b 100644 --- a/readme.md +++ b/readme.md @@ -3121,7 +3121,7 @@ justification - left, right, center text_color - color of text 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 +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 size - (None, number of rows) - don't use, use num_rows instead enable_events - will return a 'row selected' event when row is selected @@ -4781,7 +4781,7 @@ Emergency patch release... going out same day as previous release ## 3.20.0 & 1.20.0 18-Dec-2018 * New Pane Element -* Graphh.DeleteFigure method +* Graph.DeleteFigure method * disable_minimize - New parameter for Window * Fix for 2.7 menus * Debug Window no longer re-routes stdout by default @@ -4814,7 +4814,7 @@ Emergency patch release... going out same day as previous release * Attempted to use Styles better with Combobox * Fixed bug blocking setting bar colors in OneLineProgressMeter -# 3.22.0 PySimpleGUI +# 3.22.0 PySimpleGUI / 1.22.0 PySimpleGUI27 * Added type hints to some portions of the code * Output element can be made invisible @@ -4822,13 +4822,13 @@ Emergency patch release... going out same day as previous release * Invisibility for ButtonMenus * Attempt at specifying size of Column elements (limited success) * Table Element - * New row_colors paramter - * New vertical_scroll_only parameter + * New row_colors parameter + * New vertical_scroll_only parameter - NOTE - will have to disable to get horizontal scrollbars * Tree Element - * New row_height paramter + * New row_height parameter * New feature - Icons for tree entries using filename or Base64 images * Fix for bug sending back continuous mouse events -* New paramter silence_on_error for FindElement / Element calls +* New parameter silence_on_error for FindElement / Element calls * Slider returns float now * Fix for Menus when using Python 2.7 * Combobox Styling (again) @@ -4941,5 +4941,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 2395d9572d2840b9caeb114d07e4319a41761854 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:31:14 -0500 Subject: [PATCH 07/15] readme.md updated from https://stackedit.io/ --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index f02db25b..cab8448e 100644 --- a/readme.md +++ b/readme.md @@ -4431,7 +4431,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. -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. @@ -4941,5 +4941,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 79609105759f85c964c0d4d6c609628b2a416d4e Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:32:50 -0500 Subject: [PATCH 08/15] readme.md updated from https://stackedit.io/ --- readme.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index cab8448e..59396dce 100644 --- a/readme.md +++ b/readme.md @@ -3199,7 +3199,13 @@ Unlike Tables there is no standard format for trees. Thus the data structure pa * Get a TreeData Object * "Insert" data into the tree * 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. `Insert(parent_key, key, display_text, values)` @@ -4941,5 +4947,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From e3c566f9a7cec788bdd9aadd6aa48f9451c55073 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:34:30 -0500 Subject: [PATCH 09/15] readme.md updated from https://stackedit.io/ --- readme.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 59396dce..1cbe014c 100644 --- a/readme.md +++ b/readme.md @@ -3225,6 +3225,12 @@ 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. +#### 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. + + + ## Tab and Tab Group Elements @@ -4947,5 +4953,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 488d1dc797f58adf7c879edb64532fcf25a0f53f Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:36:16 -0500 Subject: [PATCH 10/15] readme.md updated from https://stackedit.io/ --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 1cbe014c..6de1aca3 100644 --- a/readme.md +++ b/readme.md @@ -3227,7 +3227,7 @@ When Reading a window the Table Element will return a list of rows that are sele #### 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. +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. @@ -4953,5 +4953,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From bd4cc84c899f51a53cbaced23161c7622ee0f0b7 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:37:57 -0500 Subject: [PATCH 11/15] readme.md updated from https://stackedit.io/ --- readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 6de1aca3..97652be0 100644 --- a/readme.md +++ b/readme.md @@ -3229,7 +3229,9 @@ When Reading a window the Table Element will return a list of rows that are sele 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 +![image](https://user-images.githubusercontent.com/13696193/51087270-2b561e80-171f-11e9-8260-6142ea9b1137.png) ## Tab and Tab Group Elements @@ -4953,5 +4955,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From ac7dbac26cac1cb64a2678217d3d5f62b2b5a56a Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:38:23 -0500 Subject: [PATCH 12/15] readme.md updated from https://stackedit.io/ --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 97652be0..47cc46aa 100644 --- a/readme.md +++ b/readme.md @@ -3229,7 +3229,7 @@ When Reading a window the Table Element will return a list of rows that are sele 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 +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) @@ -4955,5 +4955,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 009a9f385311456cd80f5fb7cb6534edf3528d15 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:40:02 -0500 Subject: [PATCH 13/15] readme.md updated from https://stackedit.io/ --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 47cc46aa..bf1328a7 100644 --- a/readme.md +++ b/readme.md @@ -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.Disable() - Use to disable the window inputwhen opening another window on top of the primnary 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=None) - Returns the element that has a matching key value window.Move(x,y) - Moves window to location x,y on screen' window.SetAlpha(alpha) - Changes window transparency window.BringToFront() - Brings the window to the top of other windows on the screen @@ -4955,5 +4955,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From 8f56a7e64588c4d47b99ef6566b3c9c34c939672 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sun, 13 Jan 2019 10:41:43 -0500 Subject: [PATCH 14/15] readme.md updated from https://stackedit.io/ --- readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index bf1328a7..8846c8c1 100644 --- a/readme.md +++ b/readme.md @@ -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.Disable() - Use to disable the window inputwhen opening another window on top of the primnary Window window.Enable() - Re-enable a Disabled window - window.FindElement(key, silent_on_error=None) - 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.SetAlpha(alpha) - Changes window transparency window.BringToFront() - Brings the window to the top of other windows on the screen @@ -1532,6 +1532,9 @@ window = sg.Window('My window title').Layout(layout) #### Finalize() 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_ _ ') @@ -4955,5 +4958,5 @@ In the hands of a competent programmer, this tool is **amazing**. It's a must- 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. \ No newline at end of file From c4ba6f35b1bd299a5fd64b4d2e7fb25bfa67a7e2 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 13 Jan 2019 12:36:52 -0500 Subject: [PATCH 15/15] Release 3.22.0 --- PySimpleGUI.py | 12 +- PySimpleGUI27.py | 327 ++++++++++++++++++++++++++++------------------- 2 files changed, 202 insertions(+), 137 deletions(-) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 96231899..b9d6ec96 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -17,6 +17,7 @@ else: import types import datetime +import time import textwrap import pickle import calendar @@ -26,7 +27,6 @@ g_time_start = 0 g_time_end = 0 g_time_delta = 0 -import time def TimerStart(): @@ -3309,7 +3309,7 @@ class TreeData(object): def __init__(self): self.tree_dict = {} - self.root_node = self.Node("", "", 'root', []) + self.root_node = self.Node("", "", 'root', [], None) self.tree_dict[""] = self.root_node def _AddNode(self, key, node): @@ -3749,7 +3749,7 @@ class Window: FillFormWithValues(self, values_dict) return self - def FindElement(self, key, silent_on_error=None): + def FindElement(self, key, silent_on_error=False): element = _FindElementFromKeyInSubForm(self, key) if element is None: if not silent_on_error: @@ -5039,8 +5039,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form:Window): # print(style_name) 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) - 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' # print(unique_field) @@ -5718,7 +5719,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form:Window): except: width = element.DefaultColumnWidth treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor) - def add_treeview_data(node): # print(f'Inserting {node.key} under parent {node.parent}') if node.key != '': @@ -5728,8 +5728,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form:Window): else: photo = tk.PhotoImage(file=node.icon) 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) else: treeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, open=element.ShowExpanded) diff --git a/PySimpleGUI27.py b/PySimpleGUI27.py index 28d720df..69a6b723 100644 --- a/PySimpleGUI27.py +++ b/PySimpleGUI27.py @@ -29,15 +29,16 @@ else: import types import datetime +import time import textwrap import pickle import calendar +from random import randint g_time_start = 0 g_time_end = 0 g_time_delta = 0 -import time def TimerStart(): @@ -73,8 +74,9 @@ def TimerStop(): # ----====----====----==== 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: DEFAULT_WINDOW_ICON = DEFAULT_BASE64_ICON else: @@ -369,7 +371,7 @@ class Element(object): self.TKEntry = None self.TKImage = None - self.ParentForm = None + self.ParentForm = None # type: Window self.ParentContainer = None # will be a Form, Column, or Frame element self.TextInputDefault = None self.Position = (0, 0) # Default position Row 0, Col 0 @@ -379,7 +381,7 @@ class Element(object): self.Tooltip = tooltip self.TooltipObject = None self.Visible = visible - + self.TKRightClickMenu = None def RightClickMenuCallback(self, event): 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.insert(tk.END, value) if visible is False: - self._TKOut.pack_forget() + self._TKOut.frame.pack_forget() elif visible is True: - self._TKOut.pack() + self._TKOut.frame.pack() def __del__(self): try: @@ -1649,10 +1651,15 @@ class Button(Element): self.TKButton.image = image if image_filename is not None: self.TKButton.config(highlightthickness=0) - photo = tk.PhotoImage(file=image_filename) - width, height = photo.width(), photo.height() - self.TKButton.config(image=photo, width=width, height=height) - self.TKButton.image = photo + image = tk.PhotoImage(file=image_filename) + if image_size is not None: + width, height = image_size + 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: self.TKButton.pack_forget() elif visible is True: @@ -1721,6 +1728,7 @@ class ButtonMenu(Element): self.MenuItemChosen = None self.Tearoff = tearoff self.TKButtonMenu = None + self.TKMenu = None # 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) @@ -1741,32 +1749,11 @@ class ButtonMenu(Element): if menu_definition is not None: self.TKMenu = tk.Menu(self.TKButtonMenu, tearoff=self.Tearoff) # create the menubar 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) - - def UpdateQt(self, menu_definition=None, text=None, button_color=(None, None), font=None, visible=None): - if menu_definition is not None: - menu_def = menu_definition - 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) + if visible is False: + self.TKButtonMenu.pack_forget() + elif visible is True: + self.TKButtonMenu.pack() @@ -2548,6 +2535,30 @@ class Slider(Element): 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) # # ---------------------------------------------------------------------- # @@ -3048,7 +3059,7 @@ MenuBar = Menu # another name for Menu to make it clear it's the Menu B # ---------------------------------------------------------------------- # 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, 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): ''' Table @@ -3095,12 +3106,14 @@ class Table(Element): self.RowHeight = row_height self.TKTreeview = None self.AlternatingRowColor = alternating_row_color + self.VerticalScrollOnly = vertical_scroll_only self.SelectedRows = [] self.ChangeSubmits = change_submits or enable_events self.BindReturnKey = bind_return_key self.StartingRowNumber = 0 # When displaying row numbers, where to start self.RowHeaderText = 'Row' self.RightClickMenu = right_click_menu + self.RowColors = row_colors 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) @@ -3167,7 +3180,7 @@ class Table(Element): # ---------------------------------------------------------------------- # class Tree(Element): 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 :param data: @@ -3212,6 +3225,9 @@ class Tree(Element): self.SelectedRows = [] self.ChangeSubmits = change_submits or enable_events 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, key=key, tooltip=tooltip, visible=visible) @@ -3234,13 +3250,26 @@ class Tree(Element): def add_treeview_data(self, node): # print(f'Inserting {node.key} under parent {node.parent}') if node.key != '': - self.TKTreeview.insert(node.parent, 'end', node.key, text=node.text, values=node.values, - open=self.ShowExpanded) + if node.icon: + 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: 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: children = self.TKTreeview.get_children() for i in children: @@ -3256,6 +3285,16 @@ class Tree(Element): self.TKTreeview.item(key, values=value) if text is not None: 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) if visible is False: self.TKTreeview.pack_forget() @@ -3269,26 +3308,27 @@ class Tree(Element): class TreeData(object): class Node(object): - def __init__(self, parent, key, text, values): + def __init__(self, parent, key, text, values, icon=None): self.parent = parent self.children = [] self.key = key self.text = text self.values = values + self.icon = icon def _Add(self, node): self.children.append(node) def __init__(self): self.tree_dict = {} - self.root_node = self.Node("", "", 'root', []) + self.root_node = self.Node("", "", 'root', [], None) self.tree_dict[""] = self.root_node def _AddNode(self, key, node): self.tree_dict[key] = node - def Insert(self, parent, key, text, values): - node = self.Node(parent, key, text, values) + def Insert(self, parent, key, text, values, icon=None): + node = self.Node(parent, key, text, values, icon) self.tree_dict[key] = node parent_node = self.tree_dict[parent] parent_node._Add(node) @@ -3396,7 +3436,7 @@ class Window(object): self.Font = font if font else DEFAULT_FONT self.RadioDict = {} 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.NonBlocking = False self.TKroot = None @@ -3594,6 +3634,8 @@ class Window(object): # 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 if self.LastButtonClickedWasRealtime: + self.LastButtonClickedWasRealtime = False # stops from generating events until something changes + # print(f'RTime down {self.LastButtonClicked}' ) try: rc = self.TKroot.update() @@ -3719,15 +3761,18 @@ class Window(object): FillFormWithValues(self, values_dict) return self - def FindElement(self, key): + def FindElement(self, key, silent_on_error=False): element = _FindElementFromKeyInSubForm(self, key) 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) + if not silent_on_error: + 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) + else: + return False return element Element = FindElement # Shortcut function @@ -4340,7 +4385,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): value = 0 elif element.Type == ELEM_TYPE_INPUT_SLIDER: try: - value = element.TKIntVar.get() + value = float(element.TKScale.get()) except: value = 0 elif element.Type == ELEM_TYPE_INPUT_MULTILINE: @@ -4687,10 +4732,18 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): highlightthickness=0) element.TKColFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0) else: - element.TKColFrame = tk.Frame(tk_row_frame) if element.Size != (None, None): - element.TKColFrame.configure(width=element.Size[0], height=element.Size[1]) - PackFormIntoFrame(element, element.TKColFrame, toplevel_form) + element.TKColFrame = TkFixedFrame(tk_row_frame) + 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') if element.Visible is False: element.TKColFrame.pack_forget() @@ -4946,7 +4999,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): timeout=DEFAULT_TOOLTIP_TIME) - # ------------------------- INPUT (Single Line) element ------------------------- # + # ------------------------- INPUT element ------------------------- # elif element_type == ELEM_TYPE_INPUT_TEXT: default_text = element.DefaultText element.TKStringVar = tk.StringVar() @@ -4983,7 +5036,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): AddMenuItem(top_menu, menu[1], element) element.TKRightClickMenu = top_menu element.TKEntry.bind('', element.RightClickMenuCallback) - # ------------------------- COMBO BOX (Drop Down) element ------------------------- # + # ------------------------- COMBOBOX 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: @@ -4993,66 +5046,49 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKStringVar = tk.StringVar() style_name = 'TCombobox' if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT: - # print('Combo special style', element.TextColor, element.BackgroundColor) - style_name = 'PSG.TCombobox' + # Creates 1 style per Text Color/ Background Color combination + style_name = element.TextColor + element.BackgroundColor + '.TCombobox' + # print(style_name) combostyle = tkinter.ttk.Style() - # - # combostyle.map("C.TButton", - # foreground=[('pressed', 'red'), ('active', 'blue')], - # background=[('pressed', '!disabled', 'black'), ('active', 'white')] - # ) - # combostyle.map('PSG.TCombobox', background=[('selected', 'green')]) - # combostyle.configure('PSG.TCombobox.Listbox',foreground='green') - # combostyle.map('PSG.TCombobox', foreground=[('active','purple')]) - # combostyle.map('PSG.TCombobox.textarea', foreground=[('active','purple')]) - # combostyle.map('PSG.TCombobox.rightdownarrow', arrowcolor=[('active','purple')]) - # combostyle.configure('PSG.TCombobox.TEntry', background='red') - # combostyle.configure('PSG.TCombobox', background=element.BackgroundColor) - combostyle.configure('PSG.TCombobox', foreground=element.TextColor) # WORKS - combostyle.configure('PSG.TCombobox', selectbackground='gray70') # WORKS - combostyle.configure('PSG.TCombobox', selectforeground=element.TextColor) # WORKS - # combostyle.configure('PSG.TCombobox.Listbox', background='purple') - # toplevel_form.TKroot.option_add("*TCombobox*Background", element.BackgroundColor) # WORK for drop-down list (Changes all) - # combostyle.map('PSG.TCombobox', background=[('active', 'purple'), ('disabled', 'purple')]) - # combostyle.configure('PSG.TCombobox.PopdownFrame', background=element.BackgroundColor) - # combostyle.configure('PSG.TCombobox.field', fieldbackground=element.BackgroundColor) - # combostyle.configure('PSG.TCombobox.Listbox', background=element.BackgroundColor) - # print(combostyle.element_names()) - # print(combostyle.element_options('PSG.TCombobox')) - # try: - # combostyle.theme_create('combostyle', - # settings={'TCombobox': - # {'configure': - # {'selectbackground': 'gray50', - # 'fieldbackground': element.BackgroundColor, - # 'foreground': text_color, - # 'background': element.BackgroundColor} - # }}) - # except: - # 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') + + + # Creates a unique name for each field element(Sure there is a better way to do this) + unique_field = str(time.time()).replace('.','') + '.TCombobox.field' + # unique_field = str(randint(1,50000000)) + '.TCombobox.field' + + # print(unique_field) + # Clones over the TCombobox.field element from the "alt" theme. + # This is what will allow us to change the background color without altering the whole programs theme + combostyle.element_create(unique_field, "from", "alt") + + # Create widget layout using cloned "alt" field + combostyle.layout(style_name, [ + (unique_field, {'children': [('Combobox.downarrow', {'side': 'right', 'sticky': 'ns'}), + ('Combobox.padding', + {'children': [('Combobox.focus', + {'children': [('Combobox.textarea', + {'sticky': 'nswe'})], + 'expand': '1', + 'sticky': 'nswe'})], + 'expand': '1', + 'sticky': 'nswe'})], + 'sticky': 'nswe'})]) + + # Copy default TCombobox settings + # Getting an error on this line of code + # combostyle.configure(style_name, *combostyle.configure("TCombobox")) + + # Set individual widget options + combostyle.configure(style_name, foreground=element.TextColor) + combostyle.configure(style_name, selectbackground='gray70') + combostyle.configure(style_name, fieldbackground=element.BackgroundColor) + combostyle.configure(style_name, selectforeground=element.TextColor) 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: 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=elementpad[0], pady=elementpad[1]) if element.Visible is False: element.TKCombo.pack_forget() @@ -5071,7 +5107,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKCombo['state'] = 'disabled' 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 ------------------------- # + # ------------------------- OPTION MENU Element (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: @@ -5170,7 +5206,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): AddMenuItem(top_menu, menu[1], element) element.TKRightClickMenu = top_menu element.TKText.bind('', element.RightClickMenuCallback) - # ------------------------- INPUT CHECKBOX element ------------------------- # + # ------------------------- CHECKBOX element ------------------------- # elif element_type == ELEM_TYPE_INPUT_CHECKBOX: width = 0 if auto_size_text else element_size[0] 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]) if element.Visible is False: element.TKProgressBar.TKProgressBarForReal.pack_forget() - # ------------------------- INPUT RADIO BUTTON element ------------------------- # + # ------------------------- RADIO BUTTON element ------------------------- # elif element_type == ELEM_TYPE_INPUT_RADIO: width = 0 if auto_size_text else element_size[0] default_value = element.InitialState @@ -5251,7 +5287,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKRadio.pack_forget() if element.Tooltip is not None: 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: width, height = element_size width = 0 if auto_size_text else element_size[0] @@ -5282,7 +5318,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): pad=elementpad) element._TKOut.pack(side=tk.LEFT, expand=True, fill='both') if element.Visible is False: - element._TKOut.pack_forget() + element._TKOut.frame.pack_forget() if element.Tooltip is not None: element.TooltipObject = ToolTip(element._TKOut, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) if element.RightClickMenu or toplevel_form.RightClickMenu: @@ -5313,7 +5349,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): else: element.tktext_label = tk.Label(tk_row_frame, width=width, height=height, bd=border_depth) 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 # 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) # ------------------------- TABLE element ------------------------- # elif element_type == ELEM_TYPE_TABLE: + element = element # type: Table frame = tk.Frame(tk_row_frame) height = element.NumRows @@ -5575,7 +5612,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): except: column_widths[i] = col_width if element.ColumnsToDisplay is None: - displaycolumns = element.ColumnHeadings + displaycolumns = element.ColumnHeadings if element.ColumnHeadings is not None else element.Values[0] else: displaycolumns = [] 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 element.TKTreeview = tkinter.ttk.Treeview(frame, columns=column_headings, displaycolumns=displaycolumns, show='headings', height=height, - selectmode=element.SelectMode) + selectmode=element.SelectMode,) treeview = element.TKTreeview if element.DisplayRowNumbers: treeview.heading(element.RowHeaderText, text=element.RowHeaderText) # make a dummy heading 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: width = max(column_widths[i], len(heading)) else: @@ -5601,15 +5640,23 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): width = element.ColumnWidths[i] except: width = element.DefaultColumnWidth - treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor) + # Insert values into the tree for i, value in enumerate(element.Values): if element.DisplayRowNumbers: value = [i+element.StartingRowNumber] + value - id = treeview.insert('', 'end', text=value, iid=i + 1, values=value, tag=i % 2) - if element.AlternatingRowColor is not None: - treeview.tag_configure(1, background=element.AlternatingRowColor) + id = treeview.insert('', 'end', text=value, iid=i + 1, values=value, tag=i) + if element.AlternatingRowColor is not None: # alternating colors + for row in range(0, len(element.Values), 2): + treeview.tag_configure(row, background=element.AlternatingRowColor) + if element.RowColors is not None: # individual row colors + for row_def in element.RowColors: + if len(row_def) == 2: # only background is specified + treeview.tag_configure(row_def[0], background=row_def[1]) + else: + treeview.tag_configure(row_def[0], background=row_def[2], foreground=row_def[1]) + if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: tkinter.ttk.Style().configure("Treeview", background=element.BackgroundColor, fieldbackground=element.BackgroundColor) @@ -5622,9 +5669,17 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): if element.BindReturnKey: treeview.bind('', element.treeview_double_click) treeview.bind('', element.treeview_double_click) + scrollbar = tk.Scrollbar(frame) scrollbar.pack(side=tk.RIGHT, fill='y') 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) 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('', element.RightClickMenuCallback) # ------------------------- Tree element ------------------------- # elif element_type == ELEM_TYPE_TREE: + element = element #type: Tree frame = tk.Frame(tk_row_frame) height = element.NumRows @@ -5663,7 +5719,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # ------------- GET THE TREEVIEW WIDGET ------------- element.TKTreeview = tkinter.ttk.Treeview(frame, columns=column_headings, displaycolumns=displaycolumns, show='tree headings', height=height, - selectmode=element.SelectMode, ) + selectmode=element.SelectMode) treeview = element.TKTreeview for i, heading in enumerate(element.ColumnHeadings): # Configure cols + headings treeview.heading(heading, text=heading) @@ -5675,11 +5731,19 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): except: width = element.DefaultColumnWidth treeview.column(heading, width=width * CharWidthInPixels(), anchor=anchor) - def add_treeview_data(node): # print(f'Inserting {node.key} under parent {node.parent}') 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: 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: 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.pack(side=tk.RIGHT, fill='y') scrollbar.config(command=treeview.yview) @@ -5712,6 +5779,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKTreeview.bind('', element.RightClickMenuCallback) # ------------------------- Separator element ------------------------- # elif element_type == ELEM_TYPE_SEPARATOR: + element = element # type: VerticalSeparator 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) # ------------------------- StatusBar element ------------------------- # @@ -5819,7 +5887,6 @@ def ConvertFlexToTK(MyFlexForm): # ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----# def StartupTK(my_flex_form): # global _my_windows - # ow = _my_windows.NumOpenWindows ow = Window.NumOpenWindows # print('Starting TK open Windows = {}'.format(ow)) @@ -7734,7 +7801,7 @@ def main(): frame2 = [ [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))], ]