Merge pull request #566 from MikeTheWatchGuy/Dev-latest
PySimpleGUI27 version of Read changes, NEW Demo - psutil dashboard
This commit is contained in:
		
						commit
						dc1140cf21
					
				
					 3 changed files with 250 additions and 27 deletions
				
			
		
							
								
								
									
										134
									
								
								Demo_Desktop_Widget_psutil_Dashboard.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								Demo_Desktop_Widget_psutil_Dashboard.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | import sys | ||||||
|  | import sys | ||||||
|  | if sys.version_info[0] >= 3: | ||||||
|  |     import PySimpleGUI as sg | ||||||
|  | else: | ||||||
|  |     import PySimpleGUI27 as sg | ||||||
|  | import psutil | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  |     Desktop floating widget - System status dashboard | ||||||
|  |     Uses psutil to display: | ||||||
|  |         Network I/O | ||||||
|  |         Disk I/O | ||||||
|  |         CPU Used | ||||||
|  |         Mem Used | ||||||
|  |     Information is updated once a second and is shown as an area graph that scrolls | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | GRAPH_WIDTH = 120       # each individual graph size in pixels | ||||||
|  | GRAPH_HEIGHT = 40 | ||||||
|  | 
 | ||||||
|  | class DashGraph(object): | ||||||
|  |     def __init__(self, graph_elem, starting_count, color): | ||||||
|  |         self.graph_current_item = 0 | ||||||
|  |         self.graph_elem = graph_elem | ||||||
|  |         self.prev_value = starting_count | ||||||
|  |         self.max_sent = 1 | ||||||
|  |         self.color = color | ||||||
|  | 
 | ||||||
|  |     def graph_value(self, current_value): | ||||||
|  |         delta = current_value - self.prev_value | ||||||
|  |         self.prev_value = current_value | ||||||
|  |         self.max_sent = max(self.max_sent, delta) | ||||||
|  |         percent_sent = 100 * delta / self.max_sent | ||||||
|  |         self.graph_elem.DrawLine((self.graph_current_item, 0), (self.graph_current_item, percent_sent), color=self.color) | ||||||
|  |         if self.graph_current_item >= GRAPH_WIDTH: | ||||||
|  |             self.graph_elem.Move(-1,0) | ||||||
|  |         else: | ||||||
|  |             self.graph_current_item += 1 | ||||||
|  |         return delta | ||||||
|  | 
 | ||||||
|  |     def graph_percentage_abs(self, value): | ||||||
|  |         self.graph_elem.DrawLine((self.graph_current_item, 0), (self.graph_current_item, value), color=self.color) | ||||||
|  |         if self.graph_current_item >= GRAPH_WIDTH: | ||||||
|  |             self.graph_elem.Move(-1,0) | ||||||
|  |         else: | ||||||
|  |             self.graph_current_item += 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def human_size(bytes, units=(' bytes','KB','MB','GB','TB', 'PB', 'EB')): | ||||||
|  |     """ Returns a human readable string reprentation of bytes""" | ||||||
|  |     return str(bytes) + units[0] if bytes < 1024 else human_size(bytes>>10, units[1:]) | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     # Make the layout less cluttered and allow bulk-changes to text formatting | ||||||
|  |     def Txt(text, **kwargs): | ||||||
|  |         return(sg.Text(text, font=('Helvetica 8'), **kwargs)) | ||||||
|  |     # Update a Text Element | ||||||
|  |     def Txt_Update(window, key, value): | ||||||
|  |         window.FindElement(key).Update(value) | ||||||
|  | 
 | ||||||
|  |     # ----------------  Create Window  ---------------- | ||||||
|  |     sg.ChangeLookAndFeel('Black') | ||||||
|  |     sg.SetOptions(element_padding=(0,0), margins=(1,1), border_width=0) | ||||||
|  | 
 | ||||||
|  |     layout = [[sg.Text('System Status Dashboard'+' '*18), sg.RButton('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window')], | ||||||
|  |               [sg.Column([[Txt('Net Out ', key='_NET_OUT_'), ], | ||||||
|  |                           [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_OUT_')]], pad=(2, 2)), | ||||||
|  |                sg.Column([[Txt('Net In', key='_NET_IN_'),], | ||||||
|  |                           [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_IN_')]], pad=(0, 2))], | ||||||
|  |               [sg.Column([[Txt('Disk Read', key='_DISK_READ_')], | ||||||
|  |                           [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_DISK_WRITE_')]], pad=(2,2)), | ||||||
|  |                sg.Column([[Txt('Disk Write', key='_DISK_WRITE_')], | ||||||
|  |                           [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, GRAPH_HEIGHT), background_color='black', key='_GRAPH_DISK_READ_')]], pad=(0, 2))], | ||||||
|  |               [sg.Column([[Txt('CPU Usage', key='_CPU_USAGE_')], [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_CPU_USAGE_')]], pad=(2,2)), | ||||||
|  |                sg.Column([[Txt('Memory Usage', key='_MEM_USAGE_')], [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_MEM_USAGE_')]], pad=(2, 2))]] | ||||||
|  | 
 | ||||||
|  |     window = sg.Window('PSG System Dashboard', | ||||||
|  |                        keep_on_top=True, | ||||||
|  |                        auto_size_buttons=False, | ||||||
|  |                        grab_anywhere=True, | ||||||
|  |                        no_titlebar=True, | ||||||
|  |                        default_button_element_size=(12, 1), | ||||||
|  |                        return_keyboard_events=True, | ||||||
|  |                        alpha_channel=.9, | ||||||
|  |                        use_default_focus=False, | ||||||
|  |                        ).Layout(layout) | ||||||
|  | 
 | ||||||
|  |     # setup graphs & initial values | ||||||
|  |     netio = psutil.net_io_counters() | ||||||
|  |     net_graph_in = DashGraph(window.FindElement('_GRAPH_IN_'), netio.bytes_recv, '#23a0a0') | ||||||
|  |     net_graph_out = DashGraph(window.FindElement('_GRAPH_OUT_'), netio.bytes_sent, '#56d856') | ||||||
|  | 
 | ||||||
|  |     diskio = psutil.disk_io_counters() | ||||||
|  |     disk_graph_write = DashGraph(window.FindElement('_GRAPH_DISK_WRITE_'), diskio.write_bytes, '#be45be') | ||||||
|  |     disk_graph_read = DashGraph(window.FindElement('_GRAPH_DISK_READ_'), diskio.read_bytes, '#5681d8') | ||||||
|  | 
 | ||||||
|  |     cpu_usage_graph = DashGraph(window.FindElement('_GRAPH_CPU_USAGE_'), 0, '#56d856') | ||||||
|  |     mem_usage_graph = DashGraph(window.FindElement('_GRAPH_MEM_USAGE_'), 0, '#5681d8') | ||||||
|  | 
 | ||||||
|  |     # ----------------  main loop  ---------------- | ||||||
|  |     while (True): | ||||||
|  |         # --------- Read and update window once a second-------- | ||||||
|  |         event, values = window.Read(timeout=1000) | ||||||
|  |         if event in (None, 'Exit'):         # Be nice and give an exit, expecially since there is no titlebar | ||||||
|  |             break | ||||||
|  |         # ----- Network Graphs ----- | ||||||
|  |         netio = psutil.net_io_counters() | ||||||
|  |         write_bytes = net_graph_out.graph_value(netio.bytes_sent) | ||||||
|  |         read_bytes = net_graph_in.graph_value(netio.bytes_recv) | ||||||
|  |         Txt_Update(window, '_NET_OUT_', 'Net out {}'.format(human_size(write_bytes))) | ||||||
|  |         Txt_Update(window, '_NET_IN_', 'Net In {}'.format(human_size(read_bytes))) | ||||||
|  |         # ----- Disk Graphs ----- | ||||||
|  |         diskio = psutil.disk_io_counters() | ||||||
|  |         write_bytes = disk_graph_write.graph_value(diskio.write_bytes) | ||||||
|  |         read_bytes = disk_graph_read.graph_value(diskio.read_bytes) | ||||||
|  |         Txt_Update(window, '_DISK_WRITE_', 'Disk Write {}'.format(human_size(write_bytes))) | ||||||
|  |         Txt_Update(window, '_DISK_READ_', 'Disk Read {}'.format(human_size(read_bytes))) | ||||||
|  |         # ----- CPU Graph ----- | ||||||
|  |         cpu = psutil.cpu_percent(0) | ||||||
|  |         cpu_usage_graph.graph_percentage_abs(cpu) | ||||||
|  |         Txt_Update(window, '_CPU_USAGE_', '{0:2.0f}% CPU Used'.format(cpu)) | ||||||
|  |         # ----- Memory Graph ----- | ||||||
|  |         mem_used = psutil.virtual_memory().percent | ||||||
|  |         mem_usage_graph.graph_percentage_abs(mem_used) | ||||||
|  |         Txt_Update(window, '_MEM_USAGE_', '{}% Memory Used'.format(mem_used)) | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     # the clever Red X graphic | ||||||
|  |     red_x = "R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw==" | ||||||
|  | 
 | ||||||
|  |     main() | ||||||
|  |     sys.exit(69) | ||||||
|  | @ -5402,7 +5402,10 @@ LOOK_AND_FEEL_TABLE = {'SystemDefault': | ||||||
|                                    'BUTTON': ('#E7C855', '#284B5A'), |                                    'BUTTON': ('#E7C855', '#284B5A'), | ||||||
|                                    'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, |                                    'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, | ||||||
|                                    'BORDER': 1, 'SLIDER_DEPTH': 0, |                                    'BORDER': 1, 'SLIDER_DEPTH': 0, | ||||||
|                                    'PROGRESS_DEPTH': 0}, |                                    'PROGRESS_DEPTH': 0, | ||||||
|  |                                    'ACCENT1': '#c15226', | ||||||
|  |                                    'ACCENT2': '#7a4d5f', | ||||||
|  |                                    'ACCENT3': '#889743'}, | ||||||
| 
 | 
 | ||||||
|                        'GreenTan': {'BACKGROUND': '#9FB8AD', |                        'GreenTan': {'BACKGROUND': '#9FB8AD', | ||||||
|                                     'TEXT': COLOR_SYSTEM_DEFAULT, |                                     'TEXT': COLOR_SYSTEM_DEFAULT, | ||||||
|  |  | ||||||
							
								
								
									
										138
									
								
								PySimpleGUI27.py
									
										
									
									
									
								
							
							
						
						
									
										138
									
								
								PySimpleGUI27.py
									
										
									
									
									
								
							|  | @ -1,4 +1,4 @@ | ||||||
| #!/usr/bin/python | #!/usr/bin/python3 | ||||||
| from __future__ import print_function | from __future__ import print_function | ||||||
| from __future__ import division | from __future__ import division | ||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
|  | @ -190,6 +190,9 @@ ThisRow = 555666777  # magic number | ||||||
| # DEFAULT_WINDOW_ICON = '' | # DEFAULT_WINDOW_ICON = '' | ||||||
| MESSAGE_BOX_LINE_WIDTH = 60 | MESSAGE_BOX_LINE_WIDTH = 60 | ||||||
| 
 | 
 | ||||||
|  | # Key representing a Read timeout | ||||||
|  | TIMEOUT_KEY = '__timeout__' | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # a shameful global variable. This represents the top-level window information. Needed because opening a second window is different than opening the first. | # a shameful global variable. This represents the top-level window information. Needed because opening a second window is different than opening the first. | ||||||
| class MyWindows(object): | class MyWindows(object): | ||||||
|  | @ -392,7 +395,8 @@ class Element(object): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = self.DisplayText |             self.ParentForm.LastButtonClicked = self.DisplayText | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def ReturnKeyHandler(self, event): |     def ReturnKeyHandler(self, event): | ||||||
|         MyForm = self.ParentForm |         MyForm = self.ParentForm | ||||||
|  | @ -409,7 +413,8 @@ class Element(object): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = '' |             self.ParentForm.LastButtonClicked = '' | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def ComboboxSelectHandler(self, event): |     def ComboboxSelectHandler(self, event): | ||||||
|         MyForm = self.ParentForm |         MyForm = self.ParentForm | ||||||
|  | @ -420,7 +425,8 @@ class Element(object): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = '' |             self.ParentForm.LastButtonClicked = '' | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def CheckboxHandler(self): |     def CheckboxHandler(self): | ||||||
|         MyForm = self.ParentForm |         MyForm = self.ParentForm | ||||||
|  | @ -429,7 +435,8 @@ class Element(object): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = '' |             self.ParentForm.LastButtonClicked = '' | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit() |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit() | ||||||
| 
 | 
 | ||||||
|     def TabGroupSelectHandler(self, event): |     def TabGroupSelectHandler(self, event): | ||||||
|         MyForm = self.ParentForm |         MyForm = self.ParentForm | ||||||
|  | @ -438,7 +445,8 @@ class Element(object): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = '' |             self.ParentForm.LastButtonClicked = '' | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit() |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit() | ||||||
| 
 | 
 | ||||||
|     def __del__(self): |     def __del__(self): | ||||||
|         try: |         try: | ||||||
|  | @ -895,7 +903,8 @@ class Spin(Element): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = '' |             self.ParentForm.LastButtonClicked = '' | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def __del__(self): |     def __del__(self): | ||||||
|         try: |         try: | ||||||
|  | @ -1237,6 +1246,8 @@ class Button(Element): | ||||||
|             self.ParentForm.LastButtonClicked = self.Key |             self.ParentForm.LastButtonClicked = self.Key | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = self.ButtonText |             self.ParentForm.LastButtonClicked = self.ButtonText | ||||||
|  |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()           # kick out of loop if read was called | ||||||
| 
 | 
 | ||||||
|     # -------  Button Callback  ------- # |     # -------  Button Callback  ------- # | ||||||
|     def ButtonCallBack(self): |     def ButtonCallBack(self): | ||||||
|  | @ -1270,7 +1281,7 @@ class Button(Element): | ||||||
|                     should_submit_window = True |                     should_submit_window = True | ||||||
|             except: |             except: | ||||||
|                 pass |                 pass | ||||||
|         filetypes = [] if self.FileTypes is None else self.FileTypes |         filetypes = (("ALL Files", "*.*"),) if self.FileTypes is None else self.FileTypes | ||||||
|         if self.BType == BUTTON_TYPE_BROWSE_FOLDER: |         if self.BType == BUTTON_TYPE_BROWSE_FOLDER: | ||||||
|             folder_name = tk.filedialog.askdirectory(initialdir=self.InitialFolder)  # show the 'get folder' dialog box |             folder_name = tk.filedialog.askdirectory(initialdir=self.InitialFolder)  # show the 'get folder' dialog box | ||||||
|             if folder_name != '': |             if folder_name != '': | ||||||
|  | @ -1280,8 +1291,10 @@ class Button(Element): | ||||||
|                 except: |                 except: | ||||||
|                     pass |                     pass | ||||||
|         elif self.BType == BUTTON_TYPE_BROWSE_FILE: |         elif self.BType == BUTTON_TYPE_BROWSE_FILE: | ||||||
|             file_name = tk.filedialog.askopenfilename(filetypes=filetypes, |             if sys.platform == 'darwin': | ||||||
|                                                       initialdir=self.InitialFolder)  # show the 'get file' dialog box |                 file_name = tk.filedialog.askopenfilename(initialdir=self.InitialFolder)  # show the 'get file' dialog box | ||||||
|  |             else: | ||||||
|  |                 file_name = tk.filedialog.askopenfilename(filetypes=filetypes, initialdir=self.InitialFolder)  # show the 'get file' dialog box | ||||||
|             if file_name != '': |             if file_name != '': | ||||||
|                 strvar.set(file_name) |                 strvar.set(file_name) | ||||||
|                 self.TKStringVar.set(file_name) |                 self.TKStringVar.set(file_name) | ||||||
|  | @ -1291,13 +1304,19 @@ class Button(Element): | ||||||
|             strvar.set(color) |             strvar.set(color) | ||||||
|             self.TKStringVar.set(color) |             self.TKStringVar.set(color) | ||||||
|         elif self.BType == BUTTON_TYPE_BROWSE_FILES: |         elif self.BType == BUTTON_TYPE_BROWSE_FILES: | ||||||
|             file_name = tk.filedialog.askopenfilenames(filetypes=filetypes, initialdir=self.InitialFolder) |             if sys.platform == 'darwin': | ||||||
|  |                 file_name = tk.filedialog.askopenfilenames(initialdir=self.InitialFolder) | ||||||
|  |             else: | ||||||
|  |                 file_name = tk.filedialog.askopenfilenames(filetypes=filetypes, initialdir=self.InitialFolder) | ||||||
|             if file_name != '': |             if file_name != '': | ||||||
|                 file_name = ';'.join(file_name) |                 file_name = ';'.join(file_name) | ||||||
|                 strvar.set(file_name) |                 strvar.set(file_name) | ||||||
|                 self.TKStringVar.set(file_name) |                 self.TKStringVar.set(file_name) | ||||||
|         elif self.BType == BUTTON_TYPE_SAVEAS_FILE: |         elif self.BType == BUTTON_TYPE_SAVEAS_FILE: | ||||||
|             file_name = tk.filedialog.asksaveasfilename(filetypes=filetypes, |             if sys.platform == 'darwin': | ||||||
|  |                 file_name = tk.filedialog.asksaveasfilename(initialdir=self.InitialFolder)  # show the 'get file' dialog box | ||||||
|  |             else: | ||||||
|  |                 file_name = tk.filedialog.asksaveasfilename(filetypes=filetypes, | ||||||
|                                                         initialdir=self.InitialFolder)  # show the 'get file' dialog box |                                                         initialdir=self.InitialFolder)  # show the 'get file' dialog box | ||||||
|             if file_name != '': |             if file_name != '': | ||||||
|                 strvar.set(file_name) |                 strvar.set(file_name) | ||||||
|  | @ -1311,7 +1330,8 @@ class Button(Element): | ||||||
|                 self.ParentForm.LastButtonClicked = self.ButtonText |                 self.ParentForm.LastButtonClicked = self.ButtonText | ||||||
|             self.ParentForm.FormRemainedOpen = False |             self.ParentForm.FormRemainedOpen = False | ||||||
|             self.ParentForm._Close() |             self.ParentForm._Close() | ||||||
|             self.ParentForm.TKroot.quit() |             if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |                 self.ParentForm.TKroot.quit() | ||||||
|             if self.ParentForm.NonBlocking: |             if self.ParentForm.NonBlocking: | ||||||
|                 self.ParentForm.TKroot.destroy() |                 self.ParentForm.TKroot.destroy() | ||||||
|                 _my_windows.Decrement() |                 _my_windows.Decrement() | ||||||
|  | @ -1323,7 +1343,8 @@ class Button(Element): | ||||||
|             else: |             else: | ||||||
|                 self.ParentForm.LastButtonClicked = self.ButtonText |                 self.ParentForm.LastButtonClicked = self.ButtonText | ||||||
|             self.ParentForm.FormRemainedOpen = True |             self.ParentForm.FormRemainedOpen = True | ||||||
|             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |             if self.ParentForm.CurrentlyRunningMainloop:            # if this window is running the mainloop, kick out | ||||||
|  |                 self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
|         elif self.BType == BUTTON_TYPE_CLOSES_WIN_ONLY:  # special kind of button that does not exit main loop |         elif self.BType == BUTTON_TYPE_CLOSES_WIN_ONLY:  # special kind of button that does not exit main loop | ||||||
|             self.ParentForm._Close() |             self.ParentForm._Close() | ||||||
|             if self.ParentForm.NonBlocking: |             if self.ParentForm.NonBlocking: | ||||||
|  | @ -1340,7 +1361,8 @@ class Button(Element): | ||||||
|         if should_submit_window: |         if should_submit_window: | ||||||
|             self.ParentForm.LastButtonClicked = target_element.Key |             self.ParentForm.LastButtonClicked = target_element.Key | ||||||
|             self.ParentForm.FormRemainedOpen = True |             self.ParentForm.FormRemainedOpen = True | ||||||
|             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |             if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |                 self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|  | @ -1995,7 +2017,8 @@ class Slider(Element): | ||||||
|         else: |         else: | ||||||
|             self.ParentForm.LastButtonClicked = '' |             self.ParentForm.LastButtonClicked = '' | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def __del__(self): |     def __del__(self): | ||||||
|         super().__del__() |         super().__del__() | ||||||
|  | @ -2378,7 +2401,8 @@ class Menu(Element): | ||||||
|         # print('IN MENU ITEM CALLBACK', item_chosen) |         # print('IN MENU ITEM CALLBACK', item_chosen) | ||||||
|         self.ParentForm.LastButtonClicked = item_chosen |         self.ParentForm.LastButtonClicked = item_chosen | ||||||
|         self.ParentForm.FormRemainedOpen = True |         self.ParentForm.FormRemainedOpen = True | ||||||
|         self.ParentForm.TKroot.quit()  # kick the users out of the mainloop |         if self.ParentForm.CurrentlyRunningMainloop: | ||||||
|  |             self.ParentForm.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def __del__(self): |     def __del__(self): | ||||||
|         super().__del__() |         super().__del__() | ||||||
|  | @ -2463,7 +2487,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, font=None, |                  def_col_width=10, auto_size_columns=True, max_col_width=20, select_mode=None, show_expanded=False, font=None, | ||||||
|                  justification='right', text_color=None, background_color=None, num_rows=None, pad=None, key=None, |                  justification='right', text_color=None, background_color=None, num_rows=None, pad=None, key=None, | ||||||
|                  tooltip=None): |                  tooltip=None): | ||||||
|         ''' |         ''' | ||||||
|  | @ -2496,14 +2520,22 @@ class Tree(Element): | ||||||
|         self.Justification = justification |         self.Justification = justification | ||||||
|         self.InitialState = None |         self.InitialState = None | ||||||
|         self.SelectMode = select_mode |         self.SelectMode = select_mode | ||||||
|  |         self.ShowExpanded = show_expanded | ||||||
|         self.NumRows = num_rows |         self.NumRows = num_rows | ||||||
|         self.Col0Width = col0_width |         self.Col0Width = col0_width | ||||||
|         self.TKTreeview = None |         self.TKTreeview = None | ||||||
|  |         self.SelectedRows = [] | ||||||
| 
 | 
 | ||||||
|         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) |                          key=key, tooltip=tooltip) | ||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     def treeview_selected(self, event): | ||||||
|  |         selections = self.TKTreeview.selection() | ||||||
|  |         self.SelectedRows = [x for x in selections] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     def __del__(self): |     def __del__(self): | ||||||
|         super().__del__() |         super().__del__() | ||||||
| 
 | 
 | ||||||
|  | @ -2633,6 +2665,7 @@ class Window(object): | ||||||
|         self.NonBlocking = False |         self.NonBlocking = False | ||||||
|         self.TKroot = None |         self.TKroot = None | ||||||
|         self.TKrootDestroyed = False |         self.TKrootDestroyed = False | ||||||
|  |         self.CurrentlyRunningMainloop = False | ||||||
|         self.FormRemainedOpen = False |         self.FormRemainedOpen = False | ||||||
|         self.TKAfterID = None |         self.TKAfterID = None | ||||||
|         self.ProgressBarColor = progress_bar_color |         self.ProgressBarColor = progress_bar_color | ||||||
|  | @ -2764,20 +2797,23 @@ class Window(object): | ||||||
|     def _TimeoutAlarmCallback(self): |     def _TimeoutAlarmCallback(self): | ||||||
|         # first, get the results table built |         # first, get the results table built | ||||||
|         # modify the Results table in the parent FlexForm object |         # modify the Results table in the parent FlexForm object | ||||||
|  |         # print('TIMEOUT CALLBACK') | ||||||
|         if self.TimerCancelled: |         if self.TimerCancelled: | ||||||
|  |             # print('** timer was cancelled **') | ||||||
|             return |             return | ||||||
|         self.LastButtonClicked = self.TimeoutKey |         self.LastButtonClicked = self.TimeoutKey | ||||||
|         self.FormRemainedOpen = True |         self.FormRemainedOpen = True | ||||||
|         self.TKroot.quit()  # kick the users out of the mainloop |         self.TKroot.quit()  # kick the users out of the mainloop | ||||||
| 
 | 
 | ||||||
|     def Read(self, timeout=None, timeout_key='_timeout_'): |     def Read(self, timeout=None, timeout_key=TIMEOUT_KEY): | ||||||
|         if timeout == 0: |         if timeout == 0:                            # timeout of zero runs the old readnonblocking | ||||||
|             event, values =  self.ReadNonBlocking() |             event, values =  self.ReadNonBlocking() | ||||||
|             if event is None: |             if event is None: | ||||||
|                 event = timeout_key |                 event = timeout_key | ||||||
|             if values is None: |             if values is None: | ||||||
|                 event = None |                 event = None | ||||||
|             return event, values |             return event, values                    # make event None if values was None and return | ||||||
|  |         # Read with a timeout | ||||||
|         self.Timeout = timeout |         self.Timeout = timeout | ||||||
|         self.TimeoutKey = timeout_key |         self.TimeoutKey = timeout_key | ||||||
|         self.NonBlocking = False |         self.NonBlocking = False | ||||||
|  | @ -2786,15 +2822,48 @@ class Window(object): | ||||||
|         if not self.Shown: |         if not self.Shown: | ||||||
|             self.Show() |             self.Show() | ||||||
|         else: |         else: | ||||||
|  |             # if already have a button waiting, the return previously built results | ||||||
|  |             if self.LastButtonClicked is not None and not self.LastButtonClickedWasRealtime: | ||||||
|  |                 # print(f'*** Found previous clicked saved {self.LastButtonClicked}') | ||||||
|  |                 results = BuildResults(self, False, self) | ||||||
|  |                 self.LastButtonClicked = None | ||||||
|  |                 return results | ||||||
|             InitializeResults(self) |             InitializeResults(self) | ||||||
|  |             # 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: | ||||||
|  |                 # print(f'RTime down {self.LastButtonClicked}' ) | ||||||
|  |                 try: | ||||||
|  |                     rc = self.TKroot.update() | ||||||
|  |                 except: | ||||||
|  |                     self.TKrootDestroyed = True | ||||||
|  |                     _my_windows.Decrement() | ||||||
|  |                     print('ROOT Destroyed') | ||||||
|  |                 results = BuildResults(self, False, self) | ||||||
|  |                 if results[0] != None and results[0] != timeout_key: | ||||||
|  |                    return results | ||||||
|  |                 else: | ||||||
|  |                     pass | ||||||
|  | 
 | ||||||
|  |                 # else: | ||||||
|  |                 #     print("** REALTIME PROBLEM FOUND **", results) | ||||||
|  | 
 | ||||||
|  |             # normal read blocking code.... | ||||||
|             if timeout != None: |             if timeout != None: | ||||||
|                 self.TimerCancelled = False |                 self.TimerCancelled = False | ||||||
|                 self.TKAfterID = self.TKroot.after(timeout, self._TimeoutAlarmCallback) |                 self.TKAfterID = self.TKroot.after(timeout, self._TimeoutAlarmCallback) | ||||||
|  |             self.CurrentlyRunningMainloop = True | ||||||
|  |             # print(f'In main {self.Title}') | ||||||
|             self.TKroot.mainloop() |             self.TKroot.mainloop() | ||||||
|  |             # print('Out main') | ||||||
|  |             self.CurrentlyRunningMainloop = False | ||||||
|  |             # if self.LastButtonClicked != TIMEOUT_KEY: | ||||||
|  |             #     print(f'Window {self.Title} Last button clicked = {self.LastButtonClicked}') | ||||||
|             try: |             try: | ||||||
|                 self.TKroot.after_cancel(self.TKAfterID) |                 self.TKroot.after_cancel(self.TKAfterID) | ||||||
|             except: |             except: | ||||||
|                 pass |                 pass | ||||||
|  |                 # print('** tkafter cancel failed **') | ||||||
|             self.TimerCancelled = True |             self.TimerCancelled = True | ||||||
|             if self.RootNeedsDestroying: |             if self.RootNeedsDestroying: | ||||||
|                 self.TKroot.destroy() |                 self.TKroot.destroy() | ||||||
|  | @ -2802,8 +2871,12 @@ class Window(object): | ||||||
|             # if form was closed with X |             # if form was closed with X | ||||||
|             if self.LastButtonClicked is None and self.LastKeyboardEvent is None and self.ReturnValues[0] is None: |             if self.LastButtonClicked is None and self.LastKeyboardEvent is None and self.ReturnValues[0] is None: | ||||||
|                 _my_windows.Decrement() |                 _my_windows.Decrement() | ||||||
|  |         # Determine return values | ||||||
|         if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None: |         if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None: | ||||||
|             return BuildResults(self, False, self) |             results =  BuildResults(self, False, self) | ||||||
|  |             if not self.LastButtonClickedWasRealtime: | ||||||
|  |                 self.LastButtonClicked = None | ||||||
|  |             return results | ||||||
|         else: |         else: | ||||||
|             return self.ReturnValues |             return self.ReturnValues | ||||||
| 
 | 
 | ||||||
|  | @ -2819,6 +2892,7 @@ class Window(object): | ||||||
|         except: |         except: | ||||||
|             self.TKrootDestroyed = True |             self.TKrootDestroyed = True | ||||||
|             _my_windows.Decrement() |             _my_windows.Decrement() | ||||||
|  |             # print("read failed") | ||||||
|             # return None, None |             # return None, None | ||||||
|         return BuildResults(self, False, self) |         return BuildResults(self, False, self) | ||||||
| 
 | 
 | ||||||
|  | @ -2927,7 +3001,8 @@ class Window(object): | ||||||
|             self.LastKeyboardEvent = str(event.keysym) + ':' + str(event.keycode) |             self.LastKeyboardEvent = str(event.keysym) + ':' + str(event.keycode) | ||||||
|         if not self.NonBlocking: |         if not self.NonBlocking: | ||||||
|             BuildResults(self, False, self) |             BuildResults(self, False, self) | ||||||
|         self.TKroot.quit() |         if self.CurrentlyRunningMainloop:       # quit if this is the current mainloop, otherwise don't quit! | ||||||
|  |             self.TKroot.quit() | ||||||
| 
 | 
 | ||||||
|     def _MouseWheelCallback(self, event): |     def _MouseWheelCallback(self, event): | ||||||
|         self.LastButtonClicked = None |         self.LastButtonClicked = None | ||||||
|  | @ -2935,7 +3010,8 @@ class Window(object): | ||||||
|         self.LastKeyboardEvent = 'MouseWheel:Down' if event.delta < 0 else 'MouseWheel:Up' |         self.LastKeyboardEvent = 'MouseWheel:Down' if event.delta < 0 else 'MouseWheel:Up' | ||||||
|         if not self.NonBlocking: |         if not self.NonBlocking: | ||||||
|             BuildResults(self, False, self) |             BuildResults(self, False, self) | ||||||
|         self.TKroot.quit() |         if self.CurrentlyRunningMainloop:       # quit if this is the current mainloop, otherwise don't quit! | ||||||
|  |             self.TKroot.quit() | ||||||
| 
 | 
 | ||||||
|     def _Close(self): |     def _Close(self): | ||||||
|         try: |         try: | ||||||
|  | @ -3003,6 +3079,12 @@ class Window(object): | ||||||
|         self._AlphaChannel = alpha |         self._AlphaChannel = alpha | ||||||
|         self.TKroot.attributes('-alpha', alpha) |         self.TKroot.attributes('-alpha', alpha) | ||||||
| 
 | 
 | ||||||
|  |     def BringToFront(self): | ||||||
|  |         try: | ||||||
|  |             self.TKroot.lift() | ||||||
|  |         except: | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|     def __enter__(self): |     def __enter__(self): | ||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
|  | @ -3411,6 +3493,8 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): | ||||||
|                         value = None |                         value = None | ||||||
|                 elif element.Type == ELEM_TYPE_TABLE: |                 elif element.Type == ELEM_TYPE_TABLE: | ||||||
|                     value = element.SelectedRows |                     value = element.SelectedRows | ||||||
|  |                 elif element.Type == ELEM_TYPE_TREE: | ||||||
|  |                     value = element.SelectedRows | ||||||
|             else: |             else: | ||||||
|                 value = None |                 value = None | ||||||
| 
 | 
 | ||||||
|  | @ -4398,7 +4482,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): | ||||||
|                 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) |                         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) | ||||||
| 
 | 
 | ||||||
|  | @ -4410,8 +4494,8 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): | ||||||
|                                           fieldbackground=element.BackgroundColor) |                                           fieldbackground=element.BackgroundColor) | ||||||
|                 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) | ||||||
| 
 |  | ||||||
|                 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') | ||||||
|  |                 treeview.bind("<<TreeviewSelect>>", element.treeview_selected) | ||||||
|                 if element.Tooltip is not None:  # tooltip |                 if element.Tooltip is not None:  # tooltip | ||||||
|                     element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, |                     element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, | ||||||
|                                                     timeout=DEFAULT_TOOLTIP_TIME) |                                                     timeout=DEFAULT_TOOLTIP_TIME) | ||||||
|  | @ -4530,7 +4614,9 @@ def StartupTK(my_flex_form): | ||||||
|         # my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback()) |         # my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback()) | ||||||
|     else:  # it's a blocking form |     else:  # it's a blocking form | ||||||
|         # print('..... CALLING MainLoop') |         # print('..... CALLING MainLoop') | ||||||
|  |         my_flex_form.CurrentlyRunningMainloop = True | ||||||
|         my_flex_form.TKroot.mainloop() |         my_flex_form.TKroot.mainloop() | ||||||
|  |         my_flex_form.CurrentlyRunningMainloop = False | ||||||
|         my_flex_form.TimerCancelled = True |         my_flex_form.TimerCancelled = True | ||||||
|         # print('..... BACK from MainLoop') |         # print('..... BACK from MainLoop') | ||||||
|         if not my_flex_form.FormRemainedOpen: |         if not my_flex_form.FormRemainedOpen: | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue