From 12973c4dccfa2024dbdc15fcaf4bbc4ad2e1a796 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sun, 17 Apr 2022 09:58:58 -0400 Subject: [PATCH] Cleaned up 4 "rainmeter" demos. Removed exit button, added standard right click exit, edit, versions. Made drive status handle drives being added / removed, all now automatically save their last location for next time they start, version info window pops up on top of window at current location --- .../Demo_Desktop_Widget_CPU_Dashboard.py | 18 +++-- .../Demo_Desktop_Widget_CPU_Top_Processes.py | 17 +++-- .../Demo_Desktop_Widget_Drive_Usage.py | 69 +++++++++++++++---- .../Demo_Desktop_Widget_psutil_Dashboard.py | 29 ++++---- 4 files changed, 91 insertions(+), 42 deletions(-) diff --git a/DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py b/DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py index 230ca479..d6b089d7 100644 --- a/DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py +++ b/DemoPrograms/Demo_Desktop_Widget_CPU_Dashboard.py @@ -15,7 +15,7 @@ import psutil Grab anywhere, making window easy to move around Note that the keys are tuples, with a tuple as the second item ('-KEY-', (row, col)) - Copyright 2020 PySimpleGUI + Copyright 2020, 2022 PySimpleGUI """ GRAPH_WIDTH = 120 # each individual graph size in pixels @@ -65,8 +65,7 @@ def main(location): sg.theme('Black') - layout = [[ sg.Text(sg.SYMBOL_X, enable_events=True, key='Exit', tooltip='Closes window'), - sg.Text(' CPU Core Usage')] ] + layout = [[sg.Text('CPU Core Usage', justification='c', expand_x=True)] ] # add on the graphs for rows in range(num_cores//NUM_COLS+1): @@ -86,7 +85,8 @@ def main(location): element_padding=(0,0), border_depth=0, location=location, - right_click_menu=[[''], ['Edit Me', 'Exit',]]) + enable_close_attempted_event=True, + right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT) graphs = [] @@ -101,10 +101,15 @@ def main(location): while True : # --------- Read and update window once every Polling Frequency -------- event, values = window.read(timeout=POLL_FREQUENCY) - if event in (sg.WIN_CLOSED, 'Exit'): # Be nice and give an exit + if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'): # Be nice and give an exit + sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting + break + elif event == sg.WIN_CLOSED: break elif event == 'Edit Me': sg.execute_editor(__file__) + elif event == 'Version': + sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location()) # read CPU for each core stats = psutil.cpu_percent(percpu=True) @@ -121,5 +126,6 @@ if __name__ == "__main__": location = sys.argv[1].split(',') location = (int(location[0]), int(location[1])) else: - location = (None, None) + location = sg.user_settings_get_entry('-location-', (None, None)) + main(location) \ No newline at end of file diff --git a/DemoPrograms/Demo_Desktop_Widget_CPU_Top_Processes.py b/DemoPrograms/Demo_Desktop_Widget_CPU_Top_Processes.py index 32fcbc9f..9d6b5316 100644 --- a/DemoPrograms/Demo_Desktop_Widget_CPU_Top_Processes.py +++ b/DemoPrograms/Demo_Desktop_Widget_CPU_Top_Processes.py @@ -34,11 +34,11 @@ def main(): # ---------------- Create Form ---------------- sg.theme('Black') - layout = [[sg.Text(font=('Helvetica', 20), text_color=sg.YELLOWS[0], key='-CPU PERCENT-'), sg.Push(), sg.Text(sg.SYMBOL_X, enable_events=True, key='Exit')], + layout = [[sg.Text(font=('Helvetica', 20), text_color=sg.YELLOWS[0], key='-CPU PERCENT-')], [sg.Text(size=(35, 12), font=('Courier New', 12), key='-PROCESSES-')], # size will determine how many processes shown - [sg.Text('Update every '), sg.Spin([x+1 for x in range(10)], 3, key='spin'), sg.T('seconds')]] + [sg.Text('Update every '), sg.Spin([x+1 for x in range(10)], 3, key='-SPIN-'), sg.T('seconds')]] - window = sg.Window('Top CPU Processes', layout, no_titlebar=True, keep_on_top=True,location=location, use_default_focus=False, alpha_channel=.8, grab_anywhere=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT, enable_close_attempted_event=True) + window = sg.Window('Top CPU Processes', layout, no_titlebar=True, keep_on_top=True,location=location, use_default_focus=False, alpha_channel=.8, grab_anywhere=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, enable_close_attempted_event=True) # start cpu measurement thread # using the PySimpleGUI call to start and manage the thread @@ -48,14 +48,17 @@ def main(): while True: # --------- Read and update window -------- event, values = window.read() + # print(event, values) # --------- Do Button Operations -------- if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'): sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting break - elif event == 'Edit Me': - sg.execute_editor(__file__) + if event == 'Edit Me': + sp = sg.execute_editor(__file__) + elif event == 'Version': + sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location()) elif event == '-CPU UPDATE FROM THREAD-': # indicates data from the thread has arrived - cpu_percent, procs = values[event] # the thread sends a tuple + cpu_percent, procs = values[event] # the thread sends a tuple if procs: # --------- Create dictionary of top % CPU processes. Format is name:cpu_percent -------- top = {proc.name(): proc.cpu_percent() for proc in procs} @@ -68,7 +71,7 @@ def main(): window['-CPU PERCENT-'].update(f'CPU {cpu_percent}') window['-PROCESSES-'].update(display_string) # get the timeout from the spinner - g_interval = int(values['spin']) + g_interval = int(values['-SPIN-']) window.close() diff --git a/DemoPrograms/Demo_Desktop_Widget_Drive_Usage.py b/DemoPrograms/Demo_Desktop_Widget_Drive_Usage.py index a3c75c24..4abdc705 100644 --- a/DemoPrograms/Demo_Desktop_Widget_Drive_Usage.py +++ b/DemoPrograms/Demo_Desktop_Widget_Drive_Usage.py @@ -7,6 +7,7 @@ import sys Desktop "Rainmeter" style widget - Drive usage Requires: psutil Shows a bar graph of space used for each drive partician that psutil finds + Copyright 2022 PySimpleGUI """ ALPHA = 0.7 @@ -16,13 +17,21 @@ UPDATE_FREQUENCY_MILLISECONDS = 20 * 1000 BAR_COLORS = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29') +class Globals(): + drive_list = None + def __init__(self): + return + + 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 update_window(window): + drive_list = [] particians = psutil.disk_partitions() + all_ok = True for count, part in enumerate(particians): mount = part[0] try: @@ -31,15 +40,19 @@ def update_window(window): window[('-PROG-', mount)].update_bar(int(usage.percent)) window[('-%-', mount)].update(f'{usage.percent}%') window[('-STATS-', mount)].update(f'{human_size(usage.used)} / {human_size(usage.total)} = {human_size(usage.free)} free') - except: + drive_list.append(str(mount)) + except KeyError as e: # A key error means a new drive was added + all_ok = False + except Exception as e: pass + all_ok = Globals.drive_list == drive_list and all_ok + Globals.drive_list = drive_list + return all_ok -def main(location): - sg.theme(THEME) # ---------------- Create Layout ---------------- - +def create_window(location): layout = [[sg.Text('Drive Status', font='Any 16')]] # Add a row for every partician that has a bar graph and text stats @@ -56,20 +69,48 @@ def main(location): sg.Text(f'{usage.percent}%', size=(6, 1), key=('-%-', mount)), sg.T(stats_info, size=(30, 1), key=('-STATS-', mount))]] except: pass - layout += [[sg.Text('Refresh', font='Any 8', key='-REFRESH-', enable_events=True), sg.Text('❎', enable_events=True, key='Exit Text')]] + layout += [[sg.Text('Refresh', font='Any 8', key='-REFRESH-', enable_events=True)]] # ---------------- Create Window ---------------- - window = sg.Window('Drive Status Widget', layout, location=location, keep_on_top=True, grab_anywhere=True, no_titlebar=True, alpha_channel=ALPHA, use_default_focus=False, - finalize=True) + window = sg.Window('Drive Status Widget', layout, location=location, keep_on_top=True, grab_anywhere=True, no_titlebar=True, alpha_channel=ALPHA, use_default_focus=False,right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, + finalize=True, enable_close_attempted_event=True) + return window + +def main(location): + # we rely on a key error to tell us if a drive was added. So.... we don't want pesky popups or other key erros to be shown + sg.set_options(suppress_error_popups=True, suppress_raise_key_errors=False, suppress_key_guessing=True) + + sg.theme(THEME) + window = create_window(location) update_window(window) # sets the progress bars + try: + # ---------------- Event Loop ---------------- + while True: + event, values = window.read(timeout=UPDATE_FREQUENCY_MILLISECONDS) + if event in (sg.WIN_CLOSED, sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'): + if event != sg.WIN_CLOSED: + sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting + break + + if event == 'Edit Me': + sp = sg.execute_editor(__file__) + elif event == 'Version': + sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location()) + + if not update_window(window): # update the window.. if not True then something changed and need to make a new window + window.close() + window = create_window(location) + update_window(window) + + + except Exception as e: + sg.Print('ERROR in event loop', e) + sg.popup_error_with_traceback('Crashed', e) + + sg.popup('Check the error!') + - # ---------------- Event Loop ---------------- - while True: - event, values = window.read(timeout=UPDATE_FREQUENCY_MILLISECONDS) - if event == sg.WIN_CLOSED or event.startswith('Exit'): - break - update_window(window) if __name__ == '__main__': @@ -77,6 +118,6 @@ if __name__ == '__main__': location = sys.argv[1].split(',') location = (int(location[0]), int(location[1])) else: - location = (None, None) + location = sg.user_settings_get_entry('-location-', (None, None)) main(location) diff --git a/DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py b/DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py index 41c740c6..b58ba80d 100644 --- a/DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py +++ b/DemoPrograms/Demo_Desktop_Widget_psutil_Dashboard.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import PySimpleGUI as sg import psutil -import sys """ Desktop floating widget - System status dashboard @@ -11,6 +10,7 @@ import sys CPU Used Mem Used Information is updated once a second and is shown as an area graph that scrolls + Copyright 2022 PySimpleGUI """ GRAPH_WIDTH, GRAPH_HEIGHT = 120, 40 # each individual graph size in pixels @@ -53,10 +53,11 @@ def human_size(bytes, units=(' bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')): return str(bytes) + units[0] if bytes < 1024 else human_size(bytes >> 10, units[1:]) -def main(location): +def main(): # ---------------- Create Window ---------------- sg.theme('Black') sg.set_options(element_padding=(0, 0), margins=(1, 1), border_width=0) + location = sg.user_settings_get_entry('-location-', (None, None)) def GraphColumn(name, key): layout = [ @@ -68,10 +69,8 @@ def main(location): key=key+'GRAPH_')]] return sg.Col(layout, pad=(2, 2)) - red_x = b"R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw==" layout = [ - [sg.Text('System Status Dashboard'+' '*18), - sg.Button('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window')], + [sg.Text('System Status Dashboard'+' '*18)], [GraphColumn('Net Out', '_NET_OUT_'), GraphColumn('Net In', '_NET_IN_')], [GraphColumn('Disk Read', '_DISK_READ_'), @@ -82,8 +81,8 @@ def main(location): window = sg.Window('PSG System Dashboard', layout, keep_on_top=True, grab_anywhere=True, no_titlebar=True, - return_keyboard_events=True, alpha_channel=ALPHA, - use_default_focus=False, finalize=True, location=location) + return_keyboard_events=True, alpha_channel=ALPHA, enable_close_attempted_event=True, + use_default_focus=False, finalize=True, location=location,right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT,) # setup graphs & initial values netio = psutil.net_io_counters() @@ -104,9 +103,14 @@ def main(location): while True : # --------- Read and update window once a second-------- event, values = window.read(timeout=1000) - # Be nice and give an exit, expecially since there is no titlebar - if event in (sg.WIN_CLOSED, 'Exit'): + if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'): + sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting break + elif event == 'Edit Me': + sp = sg.execute_editor(__file__) + elif event == 'Version': + sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location()) + # ----- Network Graphs ----- netio = psutil.net_io_counters() write_bytes = net_graph_out.graph_value(netio.bytes_sent) @@ -129,10 +133,5 @@ def main(location): window['_MEM_TXT_'].update('{}% Memory Used'.format(mem_used)) if __name__ == '__main__': - if len(sys.argv) > 1: - location = sys.argv[1].split(',') - location = (int(location[0]), int(location[1])) - else: - location = (None, None) - main(location) + main()