diff --git a/Docs/readme.rst b/Docs/readme.rst new file mode 100644 index 00000000..d7260a06 --- /dev/null +++ b/Docs/readme.rst @@ -0,0 +1,1174 @@ +.. figure:: https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png + :alt: pysimplegui\_logo + + pysimplegui\_logo +|Downloads| since Jul 11, 2018 # PySimpleGUI (Ver 2.7) + +Super-simple GUI to grasp... Powerfully customizable. + +Note - *Python3* is required to run PySimpleGUI. It takes advantage of +some Python3 features that do not translate well into Python2. + +Looking to take your Python code from the world of command lines and +into the convenience of a GUI? Have a Raspberry Pi with a touchscreen +that's going to waste because you don't have the time to learn a GUI +SDK? Look no further, you've found your GUI package. + +:: + + import PySimpleGUI as sg + + sg.MsgBox('Hello From PySimpleGUI!', 'This is the shortest GUI program ever!') + +.. figure:: https://user-images.githubusercontent.com/13696193/43162494-33095ece-8f59-11e8-86de-b6d8bcc5a52f.jpg + :alt: snap0136 + + snap0136 +Build beautiful customized forms that fit your specific problem. Let +PySimpleGUI solve your GUI problem while you solve the real problems. Do +you really want to plod through the mountains of code required to +program tkinter? + +.. figure:: https://user-images.githubusercontent.com/13696193/43273880-aa1955e6-90cb-11e8-94b6-673ecdb2698c.jpg + :alt: snap0156 + + snap0156 +Perhaps you're looking for a way to interact with your Raspberry Pi in a +more friendly way. The is the same form as above, except shown on a Pi. + +.. figure:: https://user-images.githubusercontent.com/13696193/43298356-9cfe9008-9123-11e8-9612-14649a2f6c7f.jpg + :alt: raspberry pi + + raspberry pi +In addition to a primary GUI, you can add a Progress Meter to your code +with ONE LINE of code. Slide this into any of your ``for`` loops and get +a nice meter like this: + +:: + + EasyProgressMeter('My meter title', current_value, max value) + +.. figure:: https://user-images.githubusercontent.com/13696193/42695896-a37eff5c-8684-11e8-8fbb-3d756655a44b.jpg + :alt: progress meter 2 + + progress meter 2 +You can build an async media player GUI with custom buttons in 30 lines +of code. + +.. figure:: https://user-images.githubusercontent.com/13696193/43161977-9ee7cace-8f57-11e8-8ff8-3ea24b69dab9.jpg + :alt: media file player + + media file player +I was frustrated by having to deal with the dos prompt when I had a +powerful Windows machine right in front of me. Why is it SO difficult to +do even the simplest of input/output to a window in Python?? + +There are a number of 'easy to use' Python GUIs, but they're **very** +limiting. PySimpleGUI takes the best of packages like ``EasyGUI``\ and +``WxSimpleGUI`` , both really handy but limited. The primary difference +between these and PySimpleGUI is that in addition to getting the simple +Message Boxes you also get the ability to make your own forms that are +highly customizeable. Don't like the standard Message Box? Then make +your own! + +Every call has optional parameters so that you can change the look and +feel. Don't like the button color? It's easy to change by adding a +button\_color parameter to your widget. + +GUI Packages with more functionality, like QT and WxPython, require +configuring and can take a ***week*** to get *reasonably familiar* with +the interfaces. Clearly there needs to be a middle ground between forms +with 1 or two input fields and a full-blown GUI. You'll be making your +own custom forms with PySimpleGUI within minutes, even Async forms. + +With a simple GUI, it becomes practical to "associate" .py files with +the python interpreter on Windows. Double click a py file and up pops a +GUI window, a more pleasant experience than opening a dos Window and +typing a command line. + +The ``PySimpleGUI`` package is focused on the ***developer***. How can +the desired result be achieved in as little and as simple code as +possible? This was the mantra used to create PySimpleGUI. How can it be +done is a Python-like way? + +:: + + Features of PySimpleGUI include: + Text + Single Line Input + Buttons including these types: + File Browse + Folder Browse + Non-closing return + Close form + Realtime + Checkboxes + Radio Buttons + Listbox + Slider + Icons + Multi-line Text Input + Scroll-able Output + Images + Progress Bar + Async/Non-Blocking Windows + Tabbed forms + Persistent Windows + Redirect Python Output/Errors to scrolling window + 'Higher level' APIs (e.g. MessageBox, YesNobox, ...) + Single-Line-Of-Coide Proress Bar & Debug Print + Complete control of colors, look and feel + Button images + +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... + +.. figure:: https://user-images.githubusercontent.com/13696193/43097412-0a4652aa-8e8a-11e8-8e09-939484e3c568.jpg + :alt: everything example + + everything example +Here is the code that produced the above screenshot. + +:: + + import PySimpleGUI as SG + + with SG.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1)) as form: + layout = [ + [SG.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25), text_color='blue')], + [SG.Text('Here is some text.... and a place to enter text')], + [SG.InputText()], + [SG.Checkbox('My first checkbox!'), SG.Checkbox('My second checkbox!', default=True)], + [SG.Radio('My first Radio! ', "RADIO1", default=True), SG.Radio('My second Radio!', "RADIO1")], + [SG.Multiline(default_text='This is the default Text shoulsd you decide not to type anything', + scale=(2, 10))], + [SG.InputCombo(['Combobox 1', 'Combobox 2'], size=(20, 3)), + SG.Slider(range=(1, 100), orientation='h', size=(35, 20), default_value=85)], + [SG.Listbox(values=['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(30, 6)), + SG.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=25), + SG.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=75), + SG.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=10)], + [SG.Text('_' * 100, size=(70, 1))], + [SG.Text('Choose Source and Destination Folders', size=(35, 1))], + [SG.Text('Source Folder', size=(15, 1), auto_size_text=False, justification='right'), SG.InputText('Source'), SG.FolderBrowse()], + [SG.Text('Destination Folder', size=(15, 1), auto_size_text=False, justification='right'), SG.InputText('Dest'), + SG.FolderBrowse()], + [SG.Submit(), SG.Cancel(), SG.SimpleButton('Customized', button_color=('white', 'green'))] + ] + + button, values = form.LayoutAndRead(layout) + +**A note on screen shots** You will see a number of different styles of +buttons, data entry fields, etc, in this readme. They were all made with +the same SDK, the only difference is in the settings that are specified +on a per-element, row, form, or global basis. One setting in particular, +border\_width, can make a big difference on the look of the form. Some +of the screenshots had a border\_width of 6, others a value of 1. + +APIs +---- + +PySimpleGUI can be broken down into 2 types of API's: \* High Level +single call functions \* Custom form functions + +Python Language Features +~~~~~~~~~~~~~~~~~~~~~~~~ + +There are a couple of Python language features that PySimpleGUI utilizes +heavily that should be understood first... \* Variable number of +arguments to a function call \* Optional parameters to a function call + +Variable Number of Arguments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The "High Level" API calls that *output* values take a variable number +of arguments so that they match a "print" statement as much as possible. +The idea is to make it simple for the programmer to output as many items +as desired and in any format. The user need not convert the variables to +be output into the strings. The PySimpleGUI functions do that for the +user. + +:: + + SG.MsgBox('Variable number of parameters example', var1, var2, "etc") + +Each new item begins on a new line in the Message Box + +.. figure:: https://user-images.githubusercontent.com/13696193/42844739-ebea22ac-89e1-11e8-8dd1-e61441325701.jpg + :alt: snap0104 + + snap0104 +Optional Parameters to a Function Call +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This feature of the Python language is utilized ***heavily*** as a +method of customizing forms and form Elements. Rather than requiring the +programmer to specify every possible option for a widget, instead only +the options the caller wants to override are specified. + +Here is the function definition for the MsgBox function. The details +aren't important. What is important is seeing that there is a long list +of potential tweaks that a caller can make. However, they don't *have* +to be specified on each and every call. + +:: + + def MsgBox(*args, + button_color=None, + button_type=MSG_BOX_OK, + auto_close=False, + auto_close_duration=None, + icon=DEFAULT_WINDOW_ICON, + line_width=MESSAGE_BOX_LINE_WIDTH, + font=None): + +If the caller wanted to change the button color to be black on yellow, +the call would look something like this: + +:: + + SG.MsgBox('This box has a custom button color', + button_color=('black', 'yellow')) + +.. figure:: https://user-images.githubusercontent.com/13696193/42844830-2d7e8b9a-89e2-11e8-8ef4-5af9e36f30f3.jpg + :alt: snap0105 + + snap0105 + +-------------- + +High Level API Calls +~~~~~~~~~~~~~~~~~~~~ + +The classic "input a value, print result" example. Often command line +programs simply take some value as input on the command line, do +something with it and then display the results. Moving from the command +line to a GUI is very simple. This code prompts user to input a line of +text and then displays that text in a messages box: + +:: + + import PySimpleGUI_local as SG + + rc = SG.GetTextBox('Title', 'Please input something') + SG.MsgBox('Results', 'The value returned from GetTextBox', rc) + +.. figure:: https://user-images.githubusercontent.com/13696193/42592930-1ca1370a-8519-11e8-907e-ad73e9be7749.jpg + :alt: GetTextBox + + GetTextBox +.. figure:: https://user-images.githubusercontent.com/13696193/42592929-1c7361ae-8519-11e8-8adc-411c1afee69f.jpg + :alt: MsgBox + + MsgBox +Message Boxes +^^^^^^^^^^^^^ + +In addition to MsgBox, you'll find a several API calls that are +shortcuts to common messages boxes. You can achieve similar results by +calling MsgBox with the correct parameters. + +The differences tend to be the number and types of buttons. Here are the +calls and the windows that are created. + +:: + + import PySimpleGUI as SG + +``SG.MsgBoxOK('This is an OK MsgBox')`` + +.. figure:: https://user-images.githubusercontent.com/13696193/42599852-8dd6914e-852e-11e8-888f-f133d787210b.jpg + :alt: msgboxok + + msgboxok +:: + + SG.MsgBoxOKCancel('This is an OK Cancel MsgBox') + +.. figure:: https://user-images.githubusercontent.com/13696193/42599858-8e8eff22-852e-11e8-8d5c-3fe99237eb7f.jpg + :alt: msgboxokcancel + + msgboxokcancel +:: + + SG.MsgBoxCancel('This is a Cancel MsgBox') + +.. figure:: https://user-images.githubusercontent.com/13696193/42599857-8e53dc4e-852e-11e8-8e83-6a8cccf8e706.jpg + :alt: msgboxcancel + + msgboxcancel +:: + + SG.MsgBoxYesNo('This is a Yes No MsgBox') + +.. figure:: https://user-images.githubusercontent.com/13696193/42599856-8e304540-852e-11e8-975d-fb2b62e94300.jpg + :alt: msgboxyesno + + msgboxyesno +:: + + SG.MsgBoxError('This is an error MsgBox') + +.. figure:: https://user-images.githubusercontent.com/13696193/42599853-8df8e078-852e-11e8-90dc-7815d69bff7e.jpg + :alt: msgbox error + + msgbox error +:: + + SG.MsgBoxAutoClose('This is an autoclose MsgBox') + +.. figure:: https://user-images.githubusercontent.com/13696193/42599855-8e147572-852e-11e8-8c23-7ec771909062.jpg + :alt: msgbox autoclose + + msgbox autoclose +:: + + SG.ScrolledTextBox(my_text, height=10) + +.. figure:: https://user-images.githubusercontent.com/13696193/42600800-a44f4562-8531-11e8-8c21-51dd70316879.jpg + :alt: scrolledtextbox + + scrolledtextbox +Take a moment to look at that last one. It's such a simple API call and +yet the result is awesome. Rather than seeing text scrolling past on +your display, you can capture that text and present it in a scrolled +interface. It's handy enough of an API call that it can also be called +using the name ``sprint`` which is easier to remember than +``ScrollectTextBox``. Your code could contain a line like: + +:: + + sprint(f'My variables values include x={x}', f'y={y}') + +This becomes a debug print of sorts that will route to a scrolled +window. + +High Level User Input +^^^^^^^^^^^^^^^^^^^^^ + +There are 3 very basic user input high-level function calls. It's +expected that for most applications, a custom input form will be +created. If you need only 1 value, then perhaps one of these high level +functions will work. - GetTextBox - GetFileBox - GetFolderBox + +``submit_clicked, value = SG.GetTextBox('Title', 'Please enter anything')`` + +.. figure:: https://user-images.githubusercontent.com/13696193/42600399-1ef66a5e-8530-11e8-9bc4-78ea839213cd.jpg + :alt: gettextbox + + gettextbox +:: + + submit_clicked, value = SG.GetFileBox('Title', 'Choose a file') + +.. figure:: https://user-images.githubusercontent.com/13696193/42600398-1ed8a122-8530-11e8-9f74-88b101efcea4.jpg + :alt: getfilebox + + getfilebox +:: + + submit_clicked, value = SG.GetPathBox('Title', 'Choose a folder') + +.. figure:: https://user-images.githubusercontent.com/13696193/42600397-1ea7cef8-8530-11e8-8d43-e1000c0933cd.jpg + :alt: getfolderbox + + getfolderbox +Progress Meter! +^^^^^^^^^^^^^^^ + +We all have loops in our code. 'Isn't it joyful waiting, watching a +counter scrolling past in a text window? How about one line of code to +get a progress meter, that contains statistics about your code? + +.. figure:: https://user-images.githubusercontent.com/13696193/42696332-dca3ca6e-8685-11e8-846b-6bee8362ee5f.jpg + :alt: progress meter 3 + + progress meter 3 +:: + + EasyProgressMeter(title, + current_value, + max_value, + *args, + orientation=None, + bar_color=DEFAULT_PROGRESS_BAR_COLOR, + button_color=None, + size=DEFAULT_PROGRESS_BAR_SIZE, + scale=(None, None), + border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH): + +Here's the one-line Progress Meter in action! + +:: + + for i in range(1,10000): + SG.EasyProgressMeter('My Meter', i+1, 10000, 'Optional message') + +That line of code resulted in this window popping up and updating. + +.. figure:: https://user-images.githubusercontent.com/13696193/42696912-a5c958b8-8687-11e8-9a7d-a390a465407a.jpg + :alt: progress meter 5 + + progress meter 5 +A meter AND fun statistics to watch while your machine grinds away, all +for the price of 1 line of code. With a little trickery you can provide +a way to break out of your loop using the Progress Meter form. The +cancel button results in a ``False`` return value from +``EasyProgressMeter``. It normally returns ``True``. + +:: + + if not SG.EasyProgressMeter('My Meter', i+1, 10000, 'Optional message'): + break + +***Be sure and add one to your loop counter*** so that your counter goes +from 1 to the max value. If you do not add one, your counter will never +hit the max value. Instead it will go from 0 to max-1. #### Debug Output +Another call in the 'Easy' families of APIs is ``EasyPrint``. It will +output to a debug window. If the debug window isn't open, then the first +call will open it. No need to do anything but stick a 'print' call in +your code. You can even replace your 'print' calls with calls to +EasyPrint by simply sticking the statement + +:: + + print = SG.EasyPrint + +at the top of your code. There are a number of names for the same +EasyPrint function. ``Print`` is one of the better ones to use as it's +easy to remember. It is simply ``print`` with a capital P. + +:: + + import PySimpleGUI as SG + + for i in range(100): + SG.Print(i) + +|snap0125| Or if you didn't want to change your code: + +:: + + import PySimpleGUI as SG + + print=SG.Print + for i in range(100): + print(i) + +Just like the standard print call, ``EasyPrint`` supports the ``sep`` +and ``end`` keyword arguments. Other names that can be used to call +``EasyPrint`` include Print, ``eprint``, If you want to close the +window, call the function ``EasyPrintClose``. + +A word of caution. There are known problems when multiple PySimpleGUI +windows are opened, particularly if the user closes them in an unusual +way. Not a reason to stay away from using it. Just something to keep in +mind if you encounter a problem. + +You can change the size of the debug window using the ``SetOptions`` +call with the ``debug_win_size`` parameter. + +All Widgets / Elements +---------------------- + +This code utilizes as many of the elements in one form as possible. + +:: + + with SG.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1)) as form: + layout = [ + [SG.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25), text_color='blue')], + [SG.Text('Here is some text.... and a place to enter text')], + [SG.InputText()], + [SG.Checkbox('My first checkbox!'), SG.Checkbox('My second checkbox!', default=True)], + [SG.Radio('My first Radio! ', "RADIO1", default=True), SG.Radio('My second Radio!', "RADIO1")], + [SG.Multiline(default_text='This is the default Text shoulsd you decide not to type anything', + scale=(2, 10))], + [SG.InputCombo(['Combobox 1', 'Combobox 2'], size=(20, 3)), + SG.Slider(range=(1, 100), orientation='h', size=(35, 20), default_value=85)], + [SG.Listbox(values=['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(30, 6)), + SG.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=25), + SG.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=75), + SG.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=10)], + [SG.Text('_' * 100, size=(70, 1))], + [SG.Text('Choose Source and Destination Folders', size=(35, 1))], + [SG.Text('Source Folder', size=(15, 1), auto_size_text=False, justification='right'), SG.InputText('Source'), SG.FolderBrowse()], + [SG.Text('Destination Folder', size=(15, 1), auto_size_text=False, justification='right'), SG.InputText('Dest'), + SG.FolderBrowse()], + [SG.Submit(), SG.Cancel(), SG.SimpleButton('Customized', button_color=('white', 'green'))] + ] + + button, values = form.LayoutAndRead(layout) + +This is a somewhat complex form with quite a bit of custom sizing to +make things line up well. This is code you only have to write once. When +looking at the code, remember that what you're seeing is a list of +lists. Each row contains a list of Graphical Elements that are used to +create the form. + +.. figure:: https://user-images.githubusercontent.com/13696193/43097412-0a4652aa-8e8a-11e8-8e09-939484e3c568.jpg + :alt: everything example + + everything example +Clicking the Submit button caused the form call to return. The call to +MsgBox resulted in this dialog box. |results 2| + +**``Note, button value can be None``**. The value for ``button`` will be +the text that is displayed on the button element when it was created. If +the user closed the form using something other than a button, then +``button`` will be ``None``. + +You can see in the MsgBox that the values returned are a list. Each +input field in the form generates one item in the return values list. +All input fields return a ``string`` except for Check Boxes and Radio +Buttons. These return ``bool``. + +ProgressBar +^^^^^^^^^^^ + +The ``ProgressBar`` element is used to build custom Progress Bar forms. +It is HIGHLY recommended that you use the functions that provide a +complete progress meter solution for you. Progress Meters are not easy +to work with because the forms have to be non-blocking and they are +tricky to debug. + +The **easiest** way to get progress meters into your code is to use the +``EasyProgessMeter`` API. This consists of a pair of functions, +``EasyProgessMeter`` and ``EasyProgressMeterCancel``. You can easily +cancel any progress meter by calling it with the current value = max +value. This will mark the meter as expired and close the window. You've +already seen EasyProgressMeter calls presented earlier in this readme. + +:: + + SG.EasyProgressMeter('My Meter', i+1, 1000, 'Optional message') + +The return value for ``EasyProgressMeter`` is: ``True`` if meter updated +correctly ``False`` if user clicked the Cancel button, closed the form, +or vale reached the max value. **Customized Progress Bar** If you want a +bit more customization of your meter, then you can go up 1 level and use +the calls to ``ProgressMeter`` and ``ProgressMeterUpdate``. These APIs +behave like an object we're all used to. First you create the +``ProgressMeter`` object, then you call the ``Update`` method to update +it. + +You setup the progress meter by calling + +:: + + my_meter = ProgressMeter(title, + max_value, + *args, + orientantion=None, + bar_color=DEFAULT_PROGRESS_BAR_COLOR, + button_color=None, + size=DEFAULT_PROGRESS_BAR_SIZE, + scale=(None, None), + border_width=DEFAULT_PROGRESS_BAR_BORDER_WIDTH) + +Then to update the bar within your loop + +:: + + return_code = ProgressMeterUpdate(my_meter, + value, + *args): + +Putting it all together you get this design pattern + +:: + + my_meter = SG.ProgressMeter('Meter Title', 100000, orentation='Vert') + + for i in range(0, 100000): + SG.ProgressMeterUpdate(my_meter, i+1, 'Some variable', 'Another variable') + +The final way of using a Progress Meter with PySimpleGUI is to build a +custom form with a ``ProgressBar`` Element in the form. You will need to +run your form as a non-blocking form. When you are ready to update your +progress bar, you call the ``UpdateBar`` method for the ``ProgressBar`` +element itself. + +Output +^^^^^^ + +The Output Element is a re-direction of Stdout. Anything "printed" will +be displayed in this element. + +:: + + Output(scale=(None, None), + size=(None, None)) + +Here's a complete solution for a chat-window using an Async form with an +Output Element + +:: + + import PySimpleGUI as SG + # Blocking form that doesn't close + def ChatBot(): + with SG.FlexForm('Chat Window', auto_size_text=True, default_element_size=(30, 2)) as form: + layout = [[(SG.Text('This is where standard out is being routed', size=[40, 1]))], + [SG.Output(size=(80, 20))], + [SG.Multiline(size=(70, 5), enter_submits=True), SG.ReadFormButton('SEND', button_color=(SG.YELLOWS[0], SG.BLUES[0])), SG.SimpleButton('EXIT', button_color=(SG.YELLOWS[0], SG.GREENS[0]))]] + # notice this is NOT the usual LayoutAndRead call because you don't yet want to read the form + # if you call LayoutAndRead from here, then you will miss the first button click + form.Layout(layout) + # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # + while True: + button, value = form.Read() + if button == 'SEND': + print(value) + else: + break + +Tabbed Forms +------------ + +Tabbed forms are shown using the ``ShowTabbedForm`` call. The call has +the format + +:: + + results = ShowTabbedForm('Title for the form', + (form,layout,'Tab 1 label'), + (form2,layout2, 'Tab 2 label'), ...) + +Each of the tabs of the form is in fact a form. The same steps are taken +to create the form as before. A ``FlexForm`` is created, then rows are +filled with Elements, and finally the form is shown. When calling +``ShowTabbedForm``, each form is passed in as a tuple. The tuple has the +format: ``(the form, the rows, a string shown on the tab)`` + +Results are returned as a list of lists. For each form you'll get a list +that's in the same format as a normal form. A single tab's values would +be: + +:: + + (button, (values)) + +Recall that values is a list as well. Multiple tabs in the form would +return like this: + +:: + + ((button1, (values1)), (button2, (values2)) + +## Colors ## Starting in version 2.5 you can change the background +colors for the window and the Elements. + +Your forms can go from this: |snap0155| + +to this... with one function call... + +.. figure:: https://user-images.githubusercontent.com/13696193/43273880-aa1955e6-90cb-11e8-94b6-673ecdb2698c.jpg + :alt: snap0156 + + snap0156 +While you can do it on an element by element or form level basis, the +easiest way, by far, is a call to ``SetOptions``. + +Be aware that once you change these options they are changed for the +rest of your program's execution. All of your forms will have that look +and feel, until you change it to something else (which could be the +system default colors. + +This call sets all of the different color options. + +:: + + SetOptions(background_color='#9FB8AD', + text_element_background_color='#9FB8AD', + element_background_color='#9FB8AD', + scrollbar_color=None, + input_elements_background_color='#F7F3EC', + progress_meter_color = ('green', 'blue') + button_color=('white','#475841')) + +Global Settings +--------------- + +**Global Settings** Let's have some fun customizing! Make PySimpleGUI +look the way you want it to look. You can set the global settings using +the function ``PySimpleGUI.SetOptions``. Each option has an optional +parameter that's used to set it. + +:: + + SetOptions(icon=None + button_color=(None,None) + element_size=(None,None), + margins=(None,None), + element_padding=(None,None) + auto_size_text=None + auto_size_buttons=None + font=None + border_width=None + slider_border_width=None + slider_relief=None + slider_orientation=None + autoclose_time=None + message_box_line_width=None + progress_meter_border_depth=None + progress_meter_style=None + progress_meter_relief=None + progress_meter_color=None + progress_meter_size=None + text_justification=None + background_color=None + element_background_color=None + text_element_background_color=None + input_elements_background_color=None + scrollbar_color=None, text_color=None + debug_win_size=(None,None) + window_location=(None,None) + +Explanation of parameters + +:: + + icon - filename of icon used for taskbar and title bar + button_color - button color (foreground, background) + element_size - element size (width, height) in characters + margins - tkinter margins around outsize + element_padding - tkinter padding around each element + auto_size_text - autosize the elements to fit their text + auto_size_buttons - autosize the buttons to fit their text + font - font used for elements + border_width - amount of bezel or border around sunken or raised elements + slider_border_width - changes the way sliders look + slider_relief - changes the way sliders look + slider_orientation - changes orientation of slider + autoclose_time - time in seconds for autoclose boxes + message_box_line_width - number of characers in a line of text in message boxes + progress_meter_border_depth - amount of border around raised or lowered progress meters + progress_meter_style - style of progress meter as defined by tkinter + progress_meter_relief - relief style + progress_meter_color - color of the bar and background of progress meters + progress_meter_size - size in (characters, pixels) + background_color - Color of the main window's background + element_background_color - Background color of the elements + text_element_background_color - Text element background color + input_elements_background_color - Input fields background color + scrollbar_color - Color for scrollbars (may not always work) + text_color - Text element default text color + text_justification - justification to use on Text Elements. Values are strings - 'left', 'right', 'center' + debug_win_size - size of the Print output window + window_location - location on the screen (x,y) of window's top left cornder + +These settings apply to all forms ``SetOptions``. The Row options and +Element options will take precedence over these settings. Settings can +be thought of as levels of settings with the Form-level being the +highest and the Element-level the lowest. Thus the levels are: + +- Form level +- Row level +- Element level + +Each lower level overrides the settings of the higher level. Once +settings have been changed, they remain changed for the duration of the +program (unless changed again). + +Asynchronous (Non-Blocking) Forms +--------------------------------- + +So you want to be a wizard do ya? Well go boldly! While the majority of +GUIs are a simple exercise to "collect input values and return with +them", there are instances where we want to continue executing while the +form is open. These are "asynchronous" forms and require special +options, new SDK calls, and **great care**. With asynchronous forms the +form is shown, user input is read, but your code keeps right on +chugging. YOUR responsibility is to call ``PySimpleGUI.ReadNonBlocking`` +on a periodic basis. Once a second or more will produce a reasonably +snappy GUI. + +When do you use a non-blocking form? A couple of examples are \* A media +file player like an MP3 player \* A status dashboard that's periodically +updated \* Progress Meters - when you want to make your own progress +meters \* Output using print to a scrolled text element. Good for +debugging. + +Word of warning... version 2.2, the currently released, and upcoming +version 2.3 differ in the return code for the ``ReadNonBlocking`` call. +Previously the function returned 2 values, except when the form is +closed using the "X" which returned a single value of ``None``. The +*new* way is that ``ReadNonBlocking`` always returns 2 values. If the +user closed the form with the "X" then the return values will be None, +None. You will want to key off the second value to catch this case. The +proper code to check if the user has exited the form will be a +polling-loop that looks something like this: + +:: + + while True: + button, values = form.ReadNonBlocking() + if values is None or button == 'Quit': + break + +We're going to build an app that does the latter. It's going to update +our form with a running clock. + +The basic flow and functions you will be calling are: Setup + +:: + + form = FlexForm() + form_rows = ..... + form.LayoutAndRead(form_rows, non_blocking=True) + +Periodic refresh + +:: + + form.ReadNonBlocking() + +If you need to close the form + +:: + + form.CloseNonBlockingForm() + +Rather than the usual ``form.LayoutAndRead()`` call, we're manually +adding the rows (doing the layout) and then showing the form. After the +form is shown, you simply call ``form.ReadNonBlocking()`` every now and +then. + +When you are ready to close the form (assuming the form wasn't closed by +the user or a button click) you simply call +``form.CloseNonBlockingForm()`` + +**Example - Running timer that updates** See the sample code on the +GitHub named Demo Media Player for another example of Async Forms. We're +going to make a form and update one of the elements of that form every +.01 seconds. Here's the entire code to do that. + +:: + + import PySimpleGUI as sg + import time + + # form that doesn't block + # Make a form, but don't use context manager + form = sg.FlexForm('Running Timer', auto_size_text=True) + # Create a text element that will be updated with status information on the GUI itself + output_element = sg.Text('', size=(8, 2), font=('Helvetica', 20)) + # Create the rows + form_rows = [[sg.Text('Non-blocking GUI with updates')], + [output_element], + [sg.SimpleButton('Quit')]] + # Layout the rows of the form and perform a read. Indicate the form is non-blocking! + form.LayoutAndRead(form_rows, non_blocking=True) + + # + # Some place later in your code... + # You need to perform a ReadNonBlocking on your form every now and then or + # else it won't refresh + # + + for i in range(1, 1000): + output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100)) + button, values = form.ReadNonBlocking() + if values is None or button == 'Quit': + break + time.sleep(.01) + else: + form.CloseNonBlockingForm() + +What we have here is the same sequence of function calls as in the +description. Get a form, add rows to it, show the form, and then refresh +it every now and then. + +The new thing in this example is the call use of the Update method for +the Text Element. The first thing we do inside the loop is "update" the +text element that we made earlier. This changes the value of the text +field on the form. The new value will be displayed when +``form.ReadNonBlocking()`` is called. + +Note the ``else`` statement on the for loop. This is needed because +we're about to exit the loop while the form is still open. The user has +not closed the form using the X nor a button so it's up to the caller to +close the form using ``CloseNonBlockingForm``. + +That's it... this example follows the async design pattern well. + +Sample Applications +------------------- + +Use the example programs as a starting basis for your GUI. Copy, paste, +modify and run! The demo files are: + +``Demo Recipes.py`` - Sample forms for all major form types and +situations. This is the place to get your code template from. Includes +asynchronous forms, etc. + +``Demo DisplayHash1and256.py`` - Demonstrates using High Level API calls +to get a filename + +``Demo DupliucateFileFinder.py`` - Demonstrates High Level API to get a +folder & Easy Progress Meter to show progress of the file scanning + +``Demo HowDoI.py`` - An amazing little application. Acts as a front-end +to HowDoI. This one program could forever change how you code. It does +searches on Stack Overflow and returns the CODE found in the best answer +for your query. If anyone wants to help me package this application up, +I could use a hand. + +Fun Stuff +--------- + +Here are some things to try if you're bored or want to further customize + +**Colors - Random and predefined** To set a button or text to a random +color, use the string ``'random'`` as the color value. You can also call +``PySimpleGUI.GetRandomColor``. To get a random color pair call +``PySimpleGUI.GetRandomColorPair``. This returns a tuple containing a +random color and that color's compliment. + +**Debug Output** Be sure and check out the EasyPrint (Print) function +described in the high-level API section. Leave your code the way it is, +route your stdout and stderror to a scrolling window. + +For a fun time, add these lines to the top of your script + +:: + + import PySimpleGUI as sg + print = sg.Print + +This will turn all of your print statements into prints that display in +a window on your screen rather than to the terminal. + +**Look and Feel** Dial in the look and feel that you like with the +``SetOptions`` function. You can change all of the defaults in one +function call. One line of code to customize the entire GUI. + +**ObjToString** Ever wanted to easily display an objects contents +easily? Use ObjToString to get a nicely formatted recursive walk of your +objects. This statement: + +:: + + print(sg.ObjToSting(x)) + +And this was the output + +:: + + + abc = abc + attr12 = 12 + c = + b = + a = + attr1 = 1 + attr2 = 2 + attr3 = three + attr10 = 10 + attrx = x + +You'll quickly wonder how you ever coded without it. + +-------------- + +Known Issues +============ + +While not an "issue" this is a ***stern warning*** + +**Do not attempt** to call ``PySimpleGUI`` from multiple threads! It's ``tkinter`` based and ``tkinter`` has issues with multiple threads +----------------------------------------------------------------------------------------------------------------------------------------- + +**Progress Meters** - the visual graphic portion of the meter may be +off. May return to the native tkinter progress meter solution in the +future. Right now a "custom" progress meter is used. On the bright side, +the statistics shown are extremely accurate and can tell you something +about the performance of your code. + +**Async Forms** - these include the 'easy' forms (EasyProgressMeter and +EasyPrint/Print). If you start overlapping having Async forms open with +normal forms then things get a littler squirrelly. Still tracking down +the issues and am making it more solid every day possible. You'll know +there's an issue when you see blank form. + +**EasyPrint** - EasyPrint is a new feature that's pretty awesome. You +print and the output goes to a window, with a scroll bar, that you can +copy and paste from. Being a new feature, it's got some potential +problems. There are known interaction problems with other GUI windows. +For example, closing a Print window can also close other windows you +have open. For now, don't close your debug print window until other +windows are closed too. + +Contributing +------------ + +A MikeTheWatchGuy production... entirely responsible for this code.... +unless it causes you trouble in which case I'm not at all responsible. + +Versions +-------- + ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| Version | Description | ++===========+==================================================================================================================================================+ +| 1.0.9 | July 10, 2018 - Initial Release | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 1.0.21 | July 13, 2018 - Readme updates | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.0.0 | July 16, 2018 - ALL optional parameters renamed from CamelCase to all\_lower\_case | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.1.1 | July 18, 2018 - Global settings exposed, fixes | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.2.0 | July 20, 2018 - Image Elements, Print output | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.3.0 | July 23, 2018 - Changed form.Read return codes, Slider Elements, Listbox element. Renamed some methods but left legacy calls in place for now. | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.4.0 | July 24, 2018 - Button images. Fixes so can run on Raspberry Pi | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.5.0 | July 26, 2018 - Colors. Listbox scrollbar. tkinter Progress Bar instead of homegrown. | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.6.0 | July 27, 2018 - auto\_size\_button setting. License changed to LGPL 3+ | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2.6.5 | Aug XX, 2018 - window\_location default setting | ++-----------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Release Notes +~~~~~~~~~~~~~ + +2.3 - Sliders, Listbox's and Image elements (oh my!) + +If using Progress Meters, avoid cancelling them when you have another +window open. It could lead to future windows being blank. It's being +worked on. + +New debug printing capability. ``sg.Print`` + +2.5 Discovered issue with scroll bar on ``Output`` elements. The bar +will match size of ROW not the size of the element. Normally you never +notice this due to where on a form the ``Output`` element goes. + +Listboxes are still without scrollwheels. The mouse can drag to see more +items. The mouse scrollwheel will also scroll the list and will +``page up`` and ``page down`` keys. + +Upcoming +~~~~~~~~ + +Make suggestions people! Future release features + +Columns. How multiple columns would be specified in the SDK interface +are still being designed. + +Port to other graphic engines. Hook up the front-end interface to a +backend other than tkinter. Qt, WxPython, etc. + +Code Condition +-------------- + +:: + + Make it run + Make it right + Make it fast + +It's a recipe for success if done right. PySimpleGUI has completed the +"Make it run" phase. It's far from "right" in many ways. These are being +worked on. The module is particularly poor for PEP 8 compliance. It was +a learning exercise that turned into a somewhat complete GUI solution +for lightweight problems. + +While the internals to PySimpleGUI are a tad sketchy, the public +interfaces into the SDK are more strictly defined and comply with PEP 8 +for the most part. + +Please log bugs and suggestions in the GitHub! It will only make the +code stronger and better in the end, a good thing for us all, right? + +Design +------ + +A moment about the design-spirit of ``PySimpleGUI``. From the beginning, +this package was meant to take advantage of Python's capabilities with +the goal of programming ease. + +**Single File** While not the best programming practice, the +implementation resulted in a single file solution. Only one file is +needed, PySimpleGUI.py. You can post this file, email it, and easily +import it using one statement. + +**Functions as objects** In Python, functions behave just like object. +When you're placing a Text Element into your form, you may be sometimes +calling a function and other times declaring an object. If you use the +word Text, then you're getting an object. If you're using ``Txt``, then +you're calling a function that returns a ``Text`` object. + +**Lists** It seemed quite natural to use Python's powerful list +constructs when possible. The form is specified as a series of lists. +Each "row" of the GUI is represented as a list of Elements. When the +form read returns the results to the user, all of the results are +presented as a single list. This makes reading a form's values +super-simple to do in a single line of Python code. + +Authors +------- + +MikeTheWatchGuy + +License +------- + +GNU Lesser General Public License (LGPL 3) + + +Acknowledgments +--------------- + +- Jorj McKie was the motivator behind the entire project. His + wxsimpleGUI concepts sparked PySimpleGUI into existence +- `Fredrik Lundh `__ for his + work on ``tkinter`` + +How Do I +-------- + +Finally, I must thank the fine folks at How Do I. +https://github.com/gleitz/howdoi Their utility has forever changed the +way and pace in which I can program. I urge you to try the HowDoI.py +application here on GitHub. Trust me, **it's going to be worth the +effort!** Here are the steps to run that application + +:: + + Install howdoi: + pip install howdoi + Test your install: + python -m howdoi howdoi.py + To run it: + Python HowDoI.py + +The pip command is all there is to the setup. + +The way HowDoI works is that it uses your search term to look through +stack overflow posts. It finds the best answer, gets the code from the +answer, and presents it as a response. It gives you the correct answer +OFTEN. It's a miracle that it work SO well. For Python questions, I +simply start my query with 'Python'. Let's say you forgot how to reverse +a list in Python. When you run HowDoI and ask this question, this is +what you'll see. |snap0109| + +In the hands of a competent programmer, this tool is **amazing**. It's a +must-try kind of program that has completely changed my programming +process. I'm not afraid of asking for help! You just have to be smart +about using what you find. + +The PySimpleGUI window that the results are shown in is an 'input' field +which means you can copy and paste the results right into your code. + +.. |Downloads| image:: http://pepy.tech/badge/pysimplegui + :target: http://pepy.tech/project/pysimplegui +.. |snap0125| image:: https://user-images.githubusercontent.com/13696193/43114979-a696189e-8ecf-11e8-83c7-473fcf0ccc66.jpg +.. |results 2| image:: https://user-images.githubusercontent.com/13696193/43097502-44e3ed32-8e8a-11e8-9a51-2b8af0b1a682.jpg +.. |snap0155| image:: https://user-images.githubusercontent.com/13696193/43273879-a9fdc10a-90cb-11e8-8c20-4f6a244ebe2f.jpg +.. |snap0109| image:: https://user-images.githubusercontent.com/13696193/42916444-4199b16c-8ad3-11e8-8423-d12e61a58d3d.jpg