From bb990fca051284a624395434f2a8b2dd5d8322f5 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Wed, 12 Sep 2018 18:31:39 -0400 Subject: [PATCH] New Recipes Toolbars, Explanation of the "Event Loop" --- Demo_Spinner_Compound_Element.py | 8 ++--- docs/cookbook.md | 58 +++++++++++++++++++++++++++++++- docs/index.md | 57 +++++++++++++++++++++++++++++-- readme.md | 57 +++++++++++++++++++++++++++++-- 4 files changed, 171 insertions(+), 9 deletions(-) diff --git a/Demo_Spinner_Compound_Element.py b/Demo_Spinner_Compound_Element.py index ec13d240..49ef2cc3 100644 --- a/Demo_Spinner_Compound_Element.py +++ b/Demo_Spinner_Compound_Element.py @@ -5,11 +5,11 @@ import PySimpleGUI as sg """ sg.SetOptions(element_padding=(0,0)) -sg.ChangeLookAndFeel('Dark') +# sg.ChangeLookAndFeel('Dark') # --- Define our "Big-Button-Spinner" compound element. Has 2 buttons and an input field --- # -NewSpinner = [sg.ReadFormButton('-', size=(4,1), font='Any 12'), - sg.In('0', size=(5,1), font='Any 14', justification='r', key='spin'), - sg.ReadFormButton('+', size=(4,1), font='Any 12')] +NewSpinner = [sg.ReadFormButton('-', size=(2,1), font='Any 12'), + sg.In('0', size=(2,1), font='Any 14', justification='r', key='spin'), + sg.ReadFormButton('+', size=(2,1), font='Any 12')] # --- Define Window --- # layout = [ [sg.Text('Spinner simulation')], diff --git a/docs/cookbook.md b/docs/cookbook.md index d5a98b35..47ae0e4d 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -1004,11 +1004,23 @@ Use the upper half to generate your hash code. Then paste it into the code in t ## Desktop Floating Toolbar +#### Hiding your windows commmand window +For this and the Time & CPU Widgets you may wish to consider using a tool or technique that will hide your Windows Command Prompt window. I recommend the techniques found on this site: + +[http://www.robvanderwoude.com/battech_hideconsole.php](http://www.robvanderwoude.com/battech_hideconsole.php) + +At the moment I'm using the technique that involves wscript and a script named RunNHide.vbs. They are working beautifully. I'm using a hotkey program and launch by using this script with the command "python.exe insert_program_here.py". I guess the next widget should be one that shows all the programs launched this way so you can kill any bad ones. If you don't properly catch the exit button on your form then your while loop is going to keep on working while your window is no longer there so be careful in your code to always have exit explicitly handled. + + +### Floating toolbar + This is a cool one! (Sorry about the code pastes... I'm working in it) Impress your friends at what a tool-wizard you are by popping a custom toolbar that you keep in the corner of your screen. It stays on top of all your other windows. + + ![toolbar gray](https://user-images.githubusercontent.com/13696193/45324308-bfb73700-b51b-11e8-90e7-ab24f3d6e61d.jpg) You can easily change colors to match your background by changing a couple of parameters in the code. @@ -1091,12 +1103,13 @@ You can easily change colors to match your background by changing a couple of pa -## Desktop Floating Widget +## Desktop Floating Widget - Timer This is a little widget you can leave running on your desktop. Will hopefully see more of these for things like checking email, checking server pings, displaying system information, dashboards, etc . Much of the code is handling the button states in a fancy way. It could be much simpler if you don't change the button text based on state. +![timer](https://user-images.githubusercontent.com/13696193/45336349-26a31300-b551-11e8-8b06-d1232ff8ca10.jpg) import PySimpleGUI as sg import time @@ -1159,6 +1172,49 @@ Much of the code is handling the button states in a fancy way. It could be much form.CloseNonBlockingForm() +## Desktop Floating Widget - CPU Utilization + +Like the Timer widget above, this script can be kept running. You will need the package psutil installed in order to run this Recipe. +The spinner changes the number of seconds between reads. + + +![cpu widget 2](https://user-images.githubusercontent.com/13696193/45456096-77348080-b6b7-11e8-8906-6663c31ad0eb.jpg) + + + + import PySimpleGUI as sg + import psutil + + # ---------------- Create Form ---------------- + sg.ChangeLookAndFeel('Black') + form_rows = [[sg.Text('')], + [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], + [sg.Exit(button_color=('white', 'firebrick4'), pad=((15,0), 0)), sg.Spin([x+1 for x in range(10)], 1, key='spin')]] + # Layout the rows of the form and perform a read. Indicate the form is non-blocking! + form = sg.FlexForm('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True) + form.Layout(form_rows) + + # ---------------- main loop ---------------- + while (True): + # --------- Read and update window -------- + button, values = form.ReadNonBlocking() + + # --------- Do Button Operations -------- + if values is None or button == 'Exit': + break + try: + interval = int(values['spin']) + except: + interval = 1 + + cpu_percent = psutil.cpu_percent(interval=interval) + + # --------- Display timer in window -------- + + form.FindElement('text').Update(f'CPU {cpu_percent:02.0f}%') + + # Broke out of main loop. Close the window. + form.CloseNonBlockingForm() ## Menus diff --git a/docs/index.md b/docs/index.md index 0f1218c5..e472b237 100644 --- a/docs/index.md +++ b/docs/index.md @@ -137,8 +137,9 @@ The `PySimpleGUI` package is focused on the ***developer***. Create a custom GU Bulk form-fill operation Save / Load form to/from disk Borderless (no titlebar) windows - Always on top + Always on top windows Menus + No async programming required (no callbacks to worry about) An example of many widgets used on a single form. A little further down you'll find the TWENTY lines of code required to create this complex form. Try it if you don't believe it. Start Python, copy and paste the code below into the >>> prompt and hit enter. This will pop up... @@ -181,6 +182,7 @@ You will see a number of different styles of buttons, data entry fields, etc, in --- ### Design Goals + > Copy, Paste, Run. `PySimpleGUI's` goal with the API is to be easy on the programmer, and to function in a Python-like way. Since GUIs are visual, it was desirable for the code to visually match what's on the screen. @@ -188,12 +190,16 @@ You will see a number of different styles of buttons, data entry fields, etc, in > Be Pythonic Be Pythonic... Attempted to use language constructs in a natural way and to exploit some of Python's interesting features. Python's lists and optional parameters make PySimpleGUI work. + - Forms are represented as Python lists. - A form is a list of rows - A row is a list of elements - Return values are a list of button presses and input values. - Return values can also be represented as a dictionary - The SDK calls collapse down into a single line of Python code that presents a custom GUI and returns values +- Linear programming instead of callbacks + + ----- @@ -593,7 +599,7 @@ The third is the 'compact form'. It compacts down into 2 lines of code. One li You will use these design patterns or code templates for all of your "normal" (blocking) types of input forms. Copy it and modify it to suit your needs. This is the quickest way to get your code up and running with PySimpleGUI. This is the most basic / normal of the design patterns. -### How GUI Programming in Python Should Look? +### How GUI Programming in Python Should Look? At least for beginners Why is Python such a great teaching language and yet no GUI framework exists that lends itself to the basic building blocks of Python, the list or dictionary? PySimpleGUI set out to be a Pythonic solution to the GUI problem. Whether it achieved this goal is debatable, but it was an attempt just the same. @@ -747,6 +753,53 @@ This sample program demonstrates these 2 steps as well as how to address the ret sg.Popup(button, values, values[0], values['address'], values['phone']) + +## The Event Loop / Callback Functions + +All GUIs have a few things in common, one of them being an "event loop" of some sort. If your program shows a single form, collects the data and then executes the primary code of the program then you likely don't need an event loop. This simple scenarios are front-end GUIs where you call sg.GetFile for example and then call your program that does something with the file. + +Event Loops are used in programs where the window stays open after button presses. The program processes button clicks a loop. You often hear the term event loop when discussing embedded systems or on a Raspberry Pi. Let's take a Pi demo program as an example. This program shows a GUI window, gets button presses, and uses them to control some LEDs. It loops, reading user input and doing something with it. + +This little program has a typical Event Loop + +![pi leds](https://user-images.githubusercontent.com/13696193/45448517-8cea7b80-b6a0-11e8-8dbe-eeefea2e93c1.jpg) + + + + import PySimpleGUI as sg + layout = [[sg.T('Raspberry Pi LEDs')], + [sg.ReadFormButton('Turn LED On')], + [sg.ReadFormButton('Turn LED Off')], + [sg.Exit()]] + + form = sg.FlexForm('Raspberry Pi GUI', grab_anywhere=False) + form.Layout(layout) + + # ---- Event Loop ---- # + while True: + button, values = form.Read() + + # ---- Process Button Clicks ---- # + if button is None or button == 'Exit': + break + if button == 'Turn LED Off': + turn_LED_off() + elif button == 'Turn LED On': + turn_LED_on() + + # ---- After Event Loop ---- # + sg.Popup('Done... exiting') + + + +In the Event Loop we are reading the form and then doing a series of button compares to determine what to do based on the button that was clicks (value of `button` variable) + +The way buttons are presented to the caller in PySimpleGUI is ***not*** how *most* GUI frameworks handle button clicks. Most GUI frameworks, including tkinter, use ***callback*** functions. A function you define would be called when a button is clicked. This requires you to write code where data is shared between these callback functions. There is a lot more communications that have to happen between parts of your program. + +One of the larger hurdles for beginners to GUI programming are these callback functions. PySimpleGUI was specifically designed in a way that callbacks would not be required. There is no coordination between one function and another required. You simple read your button click and take appropriate action. + +Whether or not this is a "proper" design for GUI programs can be debated. It's not a terrible tradeoff to run your own event loop and having a functioning GUI application versus one that maybe never gets written because callback functions were too much to grasp. + --- ## All Widgets / Elements diff --git a/readme.md b/readme.md index 0f1218c5..e472b237 100644 --- a/readme.md +++ b/readme.md @@ -137,8 +137,9 @@ The `PySimpleGUI` package is focused on the ***developer***. Create a custom GU Bulk form-fill operation Save / Load form to/from disk Borderless (no titlebar) windows - Always on top + Always on top windows Menus + No async programming required (no callbacks to worry about) An example of many widgets used on a single form. A little further down you'll find the TWENTY lines of code required to create this complex form. Try it if you don't believe it. Start Python, copy and paste the code below into the >>> prompt and hit enter. This will pop up... @@ -181,6 +182,7 @@ You will see a number of different styles of buttons, data entry fields, etc, in --- ### Design Goals + > Copy, Paste, Run. `PySimpleGUI's` goal with the API is to be easy on the programmer, and to function in a Python-like way. Since GUIs are visual, it was desirable for the code to visually match what's on the screen. @@ -188,12 +190,16 @@ You will see a number of different styles of buttons, data entry fields, etc, in > Be Pythonic Be Pythonic... Attempted to use language constructs in a natural way and to exploit some of Python's interesting features. Python's lists and optional parameters make PySimpleGUI work. + - Forms are represented as Python lists. - A form is a list of rows - A row is a list of elements - Return values are a list of button presses and input values. - Return values can also be represented as a dictionary - The SDK calls collapse down into a single line of Python code that presents a custom GUI and returns values +- Linear programming instead of callbacks + + ----- @@ -593,7 +599,7 @@ The third is the 'compact form'. It compacts down into 2 lines of code. One li You will use these design patterns or code templates for all of your "normal" (blocking) types of input forms. Copy it and modify it to suit your needs. This is the quickest way to get your code up and running with PySimpleGUI. This is the most basic / normal of the design patterns. -### How GUI Programming in Python Should Look? +### How GUI Programming in Python Should Look? At least for beginners Why is Python such a great teaching language and yet no GUI framework exists that lends itself to the basic building blocks of Python, the list or dictionary? PySimpleGUI set out to be a Pythonic solution to the GUI problem. Whether it achieved this goal is debatable, but it was an attempt just the same. @@ -747,6 +753,53 @@ This sample program demonstrates these 2 steps as well as how to address the ret sg.Popup(button, values, values[0], values['address'], values['phone']) + +## The Event Loop / Callback Functions + +All GUIs have a few things in common, one of them being an "event loop" of some sort. If your program shows a single form, collects the data and then executes the primary code of the program then you likely don't need an event loop. This simple scenarios are front-end GUIs where you call sg.GetFile for example and then call your program that does something with the file. + +Event Loops are used in programs where the window stays open after button presses. The program processes button clicks a loop. You often hear the term event loop when discussing embedded systems or on a Raspberry Pi. Let's take a Pi demo program as an example. This program shows a GUI window, gets button presses, and uses them to control some LEDs. It loops, reading user input and doing something with it. + +This little program has a typical Event Loop + +![pi leds](https://user-images.githubusercontent.com/13696193/45448517-8cea7b80-b6a0-11e8-8dbe-eeefea2e93c1.jpg) + + + + import PySimpleGUI as sg + layout = [[sg.T('Raspberry Pi LEDs')], + [sg.ReadFormButton('Turn LED On')], + [sg.ReadFormButton('Turn LED Off')], + [sg.Exit()]] + + form = sg.FlexForm('Raspberry Pi GUI', grab_anywhere=False) + form.Layout(layout) + + # ---- Event Loop ---- # + while True: + button, values = form.Read() + + # ---- Process Button Clicks ---- # + if button is None or button == 'Exit': + break + if button == 'Turn LED Off': + turn_LED_off() + elif button == 'Turn LED On': + turn_LED_on() + + # ---- After Event Loop ---- # + sg.Popup('Done... exiting') + + + +In the Event Loop we are reading the form and then doing a series of button compares to determine what to do based on the button that was clicks (value of `button` variable) + +The way buttons are presented to the caller in PySimpleGUI is ***not*** how *most* GUI frameworks handle button clicks. Most GUI frameworks, including tkinter, use ***callback*** functions. A function you define would be called when a button is clicked. This requires you to write code where data is shared between these callback functions. There is a lot more communications that have to happen between parts of your program. + +One of the larger hurdles for beginners to GUI programming are these callback functions. PySimpleGUI was specifically designed in a way that callbacks would not be required. There is no coordination between one function and another required. You simple read your button click and take appropriate action. + +Whether or not this is a "proper" design for GUI programs can be debated. It's not a terrible tradeoff to run your own event loop and having a functioning GUI application versus one that maybe never gets written because callback functions were too much to grasp. + --- ## All Widgets / Elements