diff --git a/PySimpleGUIWeb/Demo Programs/Web_Color_Names_Smaller_List.py b/PySimpleGUIWeb/Demo Programs/Web_Color_Names_Smaller_List.py new file mode 100644 index 00000000..3109c450 --- /dev/null +++ b/PySimpleGUIWeb/Demo Programs/Web_Color_Names_Smaller_List.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +import sys +import PySimpleGUIWeb as sg + +""" + Color names courtesy of Big Daddy's Wiki-Python + http://www.wikipython.com/tkinter-ttk-tix/summary-information/colors/ + + Shows a big chart of colors... give it a few seconds to create it + Once large window is shown, you can click on any color and another window will popup + showing both white and black text on that color +""" + + + +COLORS = ['snow', 'ghost white', 'white smoke', 'gainsboro', 'floral white', 'old lace', + 'linen', 'antique white', 'papaya whip', 'blanched almond', 'bisque', 'peach puff', + 'navajo white', 'lemon chiffon', 'mint cream', 'azure', 'alice blue', 'lavender', + 'lavender blush', 'misty rose', 'dark slate gray', 'dim gray', 'slate gray', + 'light slate gray', 'gray', 'light gray', 'midnight blue', 'navy', 'cornflower blue', 'dark slate blue', + 'slate blue', 'medium slate blue', 'light slate blue', 'medium blue', 'royal blue', 'blue', + 'dodger blue', 'deep sky blue', 'sky blue', 'light sky blue', 'steel blue', 'light steel blue', + 'light blue', 'powder blue', 'pale turquoise', 'dark turquoise', 'medium turquoise', 'turquoise', + 'cyan', 'light cyan', 'cadet blue', 'medium aquamarine', 'aquamarine', 'dark green', 'dark olive green', + 'dark sea green', 'sea green', 'medium sea green', 'light sea green', 'pale green', 'spring green', + 'lawn green', 'medium spring green', 'green yellow', 'lime green', 'yellow green', + 'forest green', 'olive drab', 'dark khaki', 'khaki', 'pale goldenrod', 'light goldenrod yellow', + 'light yellow', 'yellow', 'gold', 'light goldenrod', 'goldenrod', 'dark goldenrod', 'rosy brown', + 'indian red', 'saddle brown', 'sandy brown', + 'dark salmon', 'salmon', 'light salmon', 'orange', 'dark orange', + 'coral', 'light coral', 'tomato', 'orange red', 'red', 'hot pink', 'deep pink', 'pink', 'light pink', + 'pale violet red', 'maroon', 'medium violet red', 'violet red', + 'medium orchid', 'dark orchid', 'dark violet', 'blue violet', 'purple', 'medium purple', + 'thistle', 'snow2', 'snow3', + 'snow4', 'seashell2', 'seashell3', 'seashell4', 'AntiqueWhite1', 'AntiqueWhite2', + 'AntiqueWhite3', 'AntiqueWhite4', 'bisque2', 'bisque3', 'bisque4', 'PeachPuff2', + 'PeachPuff3', 'PeachPuff4', 'NavajoWhite2', 'NavajoWhite3', 'NavajoWhite4', + 'LemonChiffon2', 'LemonChiffon3', 'LemonChiffon4', 'cornsilk2', 'cornsilk3', + 'cornsilk4', 'ivory2', 'ivory3', 'ivory4', 'honeydew2', 'honeydew3', 'honeydew4', + 'LavenderBlush2', 'LavenderBlush3', 'LavenderBlush4', 'MistyRose2', 'MistyRose3', + 'MistyRose4', 'azure2', 'azure3', 'azure4', 'SlateBlue1', 'SlateBlue2', 'SlateBlue3', + 'SlateBlue4', 'RoyalBlue1', 'RoyalBlue2', 'RoyalBlue3', 'RoyalBlue4', 'blue2', 'blue4', + 'DodgerBlue2', 'DodgerBlue3', 'DodgerBlue4', 'SteelBlue1', 'SteelBlue2', + 'SteelBlue3', 'SteelBlue4', 'DeepSkyBlue2', 'DeepSkyBlue3', 'DeepSkyBlue4', + 'SkyBlue1', 'SkyBlue2', 'SkyBlue3', 'SkyBlue4', 'LightSkyBlue1', 'LightSkyBlue2', + 'LightSkyBlue3', 'LightSkyBlue4', 'Slategray1', 'Slategray2', 'Slategray3', + 'Slategray4', 'LightSteelBlue1', 'LightSteelBlue2', 'LightSteelBlue3', + 'LightSteelBlue4', 'LightBlue1', 'LightBlue2', 'LightBlue3', 'LightBlue4', + 'LightCyan2', 'LightCyan3', 'LightCyan4', 'PaleTurquoise1', 'PaleTurquoise2', + 'PaleTurquoise3', 'PaleTurquoise4', 'CadetBlue1', 'CadetBlue2', 'CadetBlue3', + 'CadetBlue4', 'turquoise1', 'turquoise2', 'turquoise3', 'turquoise4', 'cyan2', 'cyan3', + 'cyan4', 'DarkSlategray1', 'DarkSlategray2', 'DarkSlategray3', 'DarkSlategray4', + 'aquamarine2', 'aquamarine4', 'DarkSeaGreen1', 'DarkSeaGreen2', 'DarkSeaGreen3', + 'DarkSeaGreen4', 'SeaGreen1', 'SeaGreen2', 'SeaGreen3', 'PaleGreen1', 'PaleGreen2', + 'PaleGreen3', 'PaleGreen4', 'SpringGreen2', 'SpringGreen3', 'SpringGreen4', + 'green2', 'green3', 'green4', 'chartreuse2', 'chartreuse3', 'chartreuse4', + 'OliveDrab1', 'OliveDrab2', 'OliveDrab4', 'DarkOliveGreen1', 'DarkOliveGreen2', + 'DarkOliveGreen3', 'DarkOliveGreen4', 'khaki1', 'khaki2', 'khaki3', 'khaki4', + 'LightGoldenrod1', 'LightGoldenrod2', 'LightGoldenrod3', 'LightGoldenrod4', + 'LightYellow2', 'LightYellow3', 'LightYellow4', 'yellow2', 'yellow3', 'yellow4', + 'gold2', 'gold3', 'gold4', 'goldenrod1', 'goldenrod2', 'goldenrod3', 'goldenrod4', + 'DarkGoldenrod1', 'DarkGoldenrod2', 'DarkGoldenrod3', 'DarkGoldenrod4', + 'RosyBrown1', 'RosyBrown2', 'RosyBrown3', 'RosyBrown4', 'IndianRed1', 'IndianRed2', + 'IndianRed3', 'IndianRed4', 'sienna1', 'sienna2', 'sienna3', 'sienna4', 'burlywood1', + 'burlywood2', 'burlywood3', 'burlywood4', 'wheat1', 'wheat2', 'wheat3', 'wheat4', 'tan1', + 'tan2', 'tan4', 'chocolate1', 'chocolate2', 'chocolate3', 'firebrick1', 'firebrick2', + 'firebrick3', 'firebrick4', 'brown1', 'brown2', 'brown3', 'brown4', 'salmon1', 'salmon2', + 'salmon3', 'salmon4', 'LightSalmon2', 'LightSalmon3', 'LightSalmon4', 'orange2', + 'orange3', 'orange4', 'DarkOrange1', 'DarkOrange2', 'DarkOrange3', 'DarkOrange4', + 'coral1', 'coral2', 'coral3', 'coral4', 'tomato2', 'tomato3', 'tomato4', 'OrangeRed2', + 'OrangeRed3', 'OrangeRed4', 'red2', 'red3', 'red4', 'DeepPink2', 'DeepPink3', 'DeepPink4', + 'HotPink1', 'HotPink2', 'HotPink3', 'HotPink4', 'pink1', 'pink2', 'pink3', 'pink4', + 'LightPink1', 'LightPink2', 'LightPink3', 'LightPink4', 'PaleVioletRed1', + 'PaleVioletRed2', 'PaleVioletRed3', 'PaleVioletRed4', 'maroon1', 'maroon2', + 'maroon3', 'maroon4', 'VioletRed1', 'VioletRed2', 'VioletRed3', 'VioletRed4', + 'magenta2', 'magenta3', 'magenta4', 'orchid1', 'orchid2', 'orchid3', 'orchid4', 'plum1', + 'plum2', 'plum3', 'plum4', 'MediumOrchid1', 'MediumOrchid2', 'MediumOrchid3', + 'MediumOrchid4', 'DarkOrchid1', 'DarkOrchid2', 'DarkOrchid3', 'DarkOrchid4', + 'purple1', 'purple2', 'purple3', 'purple4', 'MediumPurple1', 'MediumPurple2', + 'MediumPurple3', 'MediumPurple4', 'thistle1', 'thistle2', 'thistle3', 'thistle4', + 'grey1', 'grey2', 'grey3', 'grey4', 'grey5', 'grey6', 'grey7', 'grey8', 'grey9', 'grey10', + 'grey11', 'grey12', 'grey13', 'grey14', 'grey15', 'grey16', 'grey17', 'grey18', 'grey19', + 'grey20', 'grey21', 'grey22', 'grey23', 'grey24', 'grey25', 'grey26', 'grey27', 'grey28', + 'grey29', 'grey30', 'grey31', 'grey32', 'grey33', 'grey34', 'grey35', 'grey36', 'grey37', + 'grey38', 'grey39', 'grey40', 'grey42', 'grey43', 'grey44', 'grey45', 'grey46', 'grey47', + 'grey48', 'grey49', 'grey50', 'grey51', 'grey52', 'grey53', 'grey54', 'grey55', 'grey56', + 'grey57', 'grey58', 'grey59', 'grey60', 'grey61', 'grey62', 'grey63', 'grey64', 'grey65', + 'grey66', 'grey67', 'grey68', 'grey69', 'grey70', 'grey71', 'grey72', 'grey73', 'grey74', + 'grey75', 'grey76', 'grey77', 'grey78', 'grey79', 'grey80', 'grey81', 'grey82', 'grey83', + 'grey84', 'grey85', 'grey86', 'grey87', 'grey88', 'grey89', 'grey90', 'grey91', 'grey92', + 'grey93', 'grey94', 'grey95', 'grey97', 'grey98', 'grey99'] + + + + +sg.SetOptions(button_element_size=(12,1), element_padding=(0,0), auto_size_buttons=False, border_width=0) + +layout = [[sg.Text('Click on a color square to see both white and black text on that color', text_color='blue', font='Any 15')]] +row = [] +layout = [] +# -- Create primary color viewer window -- +for rows in range(40): + + row = [] + for i in range(12): + try: + color = COLORS[rows+40*i] + row.append(sg.Button(color, button_color=('black', color), key=color)) + except: + pass + layout.append(row) + + +# for i, color in enumerate(COLORS): +# row.append(sg.Button(color, button_color=('black', color), key=color)) +# if (i+1) % 12 == 0: +# layout.append(row) +# row = [] + +window = sg.Window('Color Viewer', grab_anywhere=False, font=('any 9')).Layout(layout) + +# -- Event loop -- +while True: + event, values = window.Read() + if event is None: + break + # -- Create a secondary window that shows white and black text on chosen color + layout2 =[[sg.DummyButton(event, button_color=('white', event)), sg.DummyButton(event, button_color=('black', event))]] + sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).Read(timeout=0) \ No newline at end of file diff --git a/PySimpleGUIWeb/Demo Programs/Web_Popup.py b/PySimpleGUIWeb/Demo Programs/Web_Popup.py new file mode 100644 index 00000000..f286b88f --- /dev/null +++ b/PySimpleGUIWeb/Demo Programs/Web_Popup.py @@ -0,0 +1,34 @@ +import PySimpleGUIWeb as sg + +""" + ____ + | _ \ ___ _ __ _ _ _ __ + | |_) / _ \| '_ \| | | | '_ \ + | __/ (_) | |_) | |_| | |_) | + |_| \___/| .__/ \__,_| .__/ + |_| |_| + +A Popup demonstration. A "Popup" window is shown over the main +window. Clicking OK will close the Popup and you return to main again. +""" + +print('Starting up...') + +layout = [ + [sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_')], + [sg.Input(do_not_clear=True, key='_IN_')], + [sg.Button('Show'), sg.Button('Exit'), sg.Button('Blank')] +] + +window = sg.Window('Window Title').Layout(layout) + +while True: # Event Loop + print('in event loop') + event, values = window.Read() + print(event, values) + if event is None or event == 'Exit': + break + if event == 'Show': + sg.Popup('A popup!', ' You typed ', values['_IN_']) + +window.Close() \ No newline at end of file diff --git a/PySimpleGUIWeb/Demo Programs/Web_Simple.py b/PySimpleGUIWeb/Demo Programs/Web_Simple.py new file mode 100644 index 00000000..61d5ef5d --- /dev/null +++ b/PySimpleGUIWeb/Demo Programs/Web_Simple.py @@ -0,0 +1,26 @@ +import PySimpleGUIWeb as sg + +def main(): + layout = [ + [sg.Text('This is a text element')], + [sg.Input()], + [sg.Combo(['Combo 1'])], + [sg.Text('If you close the browser tab, the app will exit gracefully')], + [sg.InputText('Source', do_not_clear=True)], + [sg.InputText('Dest', do_not_clear=True)], + [sg.Ok(), sg.Cancel()] + ] + + window = sg.Window('Demo window..').Layout(layout) + i = 0 + while True: + event, values = window.Read(timeout=1) + if event != sg.TIMEOUT_KEY: + print(event, values) + if event is None: + break + i += 1 + window.Close() + +main() +print('Program terminating normally') \ No newline at end of file diff --git a/PySimpleGUIWeb/Demo Programs/Web_Table_Element.py b/PySimpleGUIWeb/Demo Programs/Web_Table_Element.py index 8626fa5c..780ed806 100644 --- a/PySimpleGUIWeb/Demo Programs/Web_Table_Element.py +++ b/PySimpleGUIWeb/Demo Programs/Web_Table_Element.py @@ -2,6 +2,27 @@ import PySimpleGUIWeb as sg import random import string +""" +ooooooooooooo .o8 oooo +8' 888 `8 "888 `888 + 888 .oooo. 888oooo. 888 .ooooo. + 888 `P )88b d88' `88b 888 d88' `88b + 888 .oP"888 888 888 888 888ooo888 + 888 d8( 888 888 888 888 888 .o + o888o `Y888""8o `Y8bod8P' o888o `Y8bod8P' + + + +oooooooooooo oooo . +`888' `8 `888 .o8 + 888 888 .ooooo. ooo. .oo. .oo. .ooooo. ooo. .oo. .o888oo + 888oooo8 888 d88' `88b `888P"Y88bP"Y88b d88' `88b `888P"Y88b 888 + 888 " 888 888ooo888 888 888 888 888ooo888 888 888 888 + 888 o 888 888 .o 888 888 888 888 .o 888 888 888 . +o888ooooood8 o888o `Y8bod8P' o888o o888o o888o `Y8bod8P' o888o o888o "888" +""" + + # ------------------ Create a fake table ------------------ class Fake(): @classmethod @@ -40,6 +61,7 @@ window = sg.Window('Table Element Example').Layout(layout) # ------------------ The Event Loop ------------------ while True: event, values = window.Read() + print(event, values) if event in (None, 'Exit'): break window.Element('_selected_rows_').Update(values['_table_']) diff --git a/PySimpleGUIWeb/Demo Programs/Web_psutil_Kill_Processes.py b/PySimpleGUIWeb/Demo Programs/Web_psutil_Kill_Processes.py new file mode 100644 index 00000000..afeca438 --- /dev/null +++ b/PySimpleGUIWeb/Demo Programs/Web_psutil_Kill_Processes.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +import sys +import sys +import PySimpleGUIWeb as sg +import os +import signal +import psutil +import operator + + +""" + Utility to show running processes, CPU usage and provides way to kill processes. + Based on psutil package that is easily installed using pip +""" + +def kill_proc_tree(pid, sig=signal.SIGTERM, include_parent=True, + timeout=None, on_terminate=None): + """Kill a process tree (including grandchildren) with signal + "sig" and return a (gone, still_alive) tuple. + "on_terminate", if specified, is a callabck function which is + called as soon as a child terminates. + """ + if pid == os.getpid(): + raise RuntimeError("I refuse to kill myself") + parent = psutil.Process(pid) + children = parent.children(recursive=True) + if include_parent: + children.append(parent) + for p in children: + p.send_signal(sig) + gone, alive = psutil.wait_procs(children, timeout=timeout, + callback=on_terminate) + return (gone, alive) + + +def main(): + + # ---------------- Create Form ---------------- + # sg.ChangeLookAndFeel('Topanga') + + layout = [[sg.Text('Process Killer - Choose one or more processes', + size=(45,1), font=('Helvetica', 15), text_color='red')], + [sg.Listbox(values=[' '], size=(50, 30), select_mode=sg.SELECT_MODE_EXTENDED, font=('Courier', 12), key='_processes_')], + [sg.Text('Click refresh once or twice.. once for list, second to get CPU usage')], + [sg.T('Filter by typing name', font='ANY 14'), sg.In(size=(15,1), font='any 14', key='_filter_')], + [sg.Button('Sort by Name', ), + sg.Button('Sort by % CPU', button_color=('white', 'DarkOrange2')), + sg.Button('Kill', button_color=('white','red'), bind_return_key=True), + sg.Exit(button_color=('white', 'sea green'))]] + + window = sg.Window('Process Killer', + keep_on_top=True, + auto_size_buttons=False, + default_button_element_size=(12,1), + return_keyboard_events=True, + ).Layout(layout) + + display_list = None + # ---------------- main loop ---------------- + while (True): + # --------- Read and update window -------- + event, values = window.Read() + if event is None or event == 'Exit': + break + + # skip mouse, control key and shift key events entirely + if 'Mouse' in event or 'Control' in event or 'Shift' in event: + continue + print(event, values) + # --------- Do Button Operations -------- + if event == 'Sort by Name': + psutil.cpu_percent(interval=.1) + procs = psutil.process_iter() + all_procs = [[proc.cpu_percent(), proc.name(), proc.pid] for proc in procs] + sorted_by_cpu_procs = sorted(all_procs, key=operator.itemgetter(1), reverse=False) + display_list = [] + for process in sorted_by_cpu_procs: + display_list.append('{:5d} {:5.2f} {}\n'.format(process[2], process[0]/10, process[1])) + window.FindElement('_processes_').Update(display_list) + print(display_list) + elif event == 'Kill': + processes_to_kill = values['_processes_'] + for proc in processes_to_kill: + pid = int(proc[0:5]) + # if sg.PopupYesNo('About to kill {} {}'.format(pid, proc[12:]), keep_on_top=True) == 'Yes': + try: + kill_proc_tree(pid=pid) + except: + sg.PopupAutoClose('Error killing process', auto_close_duration=1) + elif event == 'Sort by % CPU': + psutil.cpu_percent(interval=.1) + procs = psutil.process_iter() + all_procs = [[proc.cpu_percent(), proc.name(), proc.pid] for proc in procs] + sorted_by_cpu_procs = sorted(all_procs, key=operator.itemgetter(0), reverse=True) + display_list = [] + for process in sorted_by_cpu_procs: + display_list.append('{:5d} {:5.2f} {}\n'.format(process[2], process[0]/10, process[1])) + window.FindElement('_processes_').Update(display_list) + else: # was a typed character + if display_list is not None: + new_output = [] + for line in display_list: + if values['_filter_'] in line.lower(): + new_output.append(line) + window.FindElement('_processes_').Update(new_output) + + +if __name__ == "__main__": + main() + sys.exit(0) \ No newline at end of file diff --git a/PySimpleGUIWeb/Demo Programs/logo.jpg b/PySimpleGUIWeb/Demo Programs/logo.jpg new file mode 100644 index 00000000..ead39fe5 Binary files /dev/null and b/PySimpleGUIWeb/Demo Programs/logo.jpg differ diff --git a/PySimpleGUIWeb/PySimpleGUIWeb.py b/PySimpleGUIWeb/PySimpleGUIWeb.py index 059aeef4..9f5fe838 100644 --- a/PySimpleGUIWeb/PySimpleGUIWeb.py +++ b/PySimpleGUIWeb/PySimpleGUIWeb.py @@ -2316,7 +2316,7 @@ class Menu(Element): # ---------------------------------------------------------------------- # 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, row_header_text='Row', 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, disabled=False, + auto_size_columns=True, max_col_width=20, select_mode=None, display_row_numbers=False, row_header_text='Row', starting_row_num=0, 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, disabled=False, size=(None, None), change_submits=False, enable_events=False, bind_return_key=False, pad=None, key=None, tooltip=None, right_click_menu=None, visible=True, size_px=(None, None)): ''' Table @@ -2367,7 +2367,7 @@ class Table(Element): 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.StartingRowNumber = starting_row_num # When displaying row numbers, where to start self.RowHeaderText = row_header_text self.RightClickMenu = right_click_menu self.RowColors = row_colors @@ -3234,11 +3234,11 @@ class Window: else: self.window = userdata2 # type: Window self.master_widget = None + self.window.App = self if userdata2 is None: res_path = os.path.dirname(os.path.abspath(__file__)) # print('res path', res_path) super(Window.MyApp, self).__init__(*args, static_file_path={'C':'c:','c':'c:','D':'d:', 'd':'d:', 'E':'e:', 'e':'e:', 'dot':'.', '.':'.'}) - self.window.App = self def main(self, name='world'): # margin 0px auto allows to center the app to the screen @@ -4699,7 +4699,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): for row_num, row in enumerate(element.Values): # convert entire table to strings new_row= [str(item) for item in row] if element.DisplayRowNumbers: - new_row = [element.RowHeaderText if row_num == 0 else str(row_num) ,] + new_row + new_row = [element.RowHeaderText if row_num == 0 else str(row_num+element.StartingRowNumber) ,] + new_row new_table.append(new_row) element.Widget = remi.gui.Table.new_from_list(new_table) do_font_and_color(element.Widget) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index b95157eb..9a1e322c 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -8,7 +8,7 @@ ![Python Version](https://img.shields.io/badge/Python-3.x-yellow.svg) -![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_-0.17.0-orange.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_-0.18.0-orange.svg?longCache=true&style=for-the-badge) @@ -264,6 +264,12 @@ New features * Can get the value of the item clicked using Table.SelectedItem. Can be coded as window.Element('_table_').SelectedItem +## 0.18.0 15-Mar-2019 + +* Hotfix for bug that causes Popups / secondary windows to crash +* Table gets `starting_row_num` parameter + + # Design # Author