PySimpleGUI27 version of Read changes, NEW Demo - psutil dashboard
This commit is contained in:
parent
4b7c9e07cf
commit
1b5cfcad73
|
@ -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…
Reference in New Issue