Metadata property added to call ref. Checkin of the latest readme_creator files
This commit is contained in:
parent
faa701e3de
commit
fcb3cc7bde
17 changed files with 19766 additions and 8351 deletions
|
@ -145,7 +145,7 @@ Preview of popups:
|
|||
<img src="https://user-images.githubusercontent.com/13696193/44957595-9e15da00-aea1-11e8-8909-6b6121b74509.jpg">
|
||||
</p>
|
||||
|
||||
<!-- <+func.Popup+> -->
|
||||
<!-- <+func.popup+> -->
|
||||
|
||||
The other output Popups are variations on parameters. Usually the button_type parameter is the primary one changed.
|
||||
|
||||
|
@ -996,15 +996,96 @@ You have a couple of options for dealing this with. If your operation can be br
|
|||
|
||||
If, on the other hand, your operation is not under your control or you are unable to add `Refresh` calls, then the next option available to you is to move your long operations into a thread.
|
||||
|
||||
### The "Old Way"
|
||||
|
||||
There are a couple of demo programs available for you to see how to do this. You basically put your work into a thread. When the thread is completed, it tells the GUI by sending a message through a queue. The event loop will run with a timer set to a value that represents how "responsive" you want your GUI to be to the work completing.
|
||||
|
||||
These 2 demo programs are called
|
||||
|
||||
### The "New Way" - `Window.write_event_value`
|
||||
|
||||
This new function that is available currently only in the tkinter port as of July 2020 is exciting and represents the future way multi-threading will be handled in PySimpleGUI (or so is hoped).
|
||||
|
||||
Previously, a queue was used where your event loop would **poll** for incoming messages from a thread.
|
||||
|
||||
Now, threads can directly inject events into a Window so that it will show up in the `window.read()` calls. This allows a your event loop to "pend", waiting for normal window events as well as events being generated by threads.
|
||||
|
||||
You can see this new capability in action in this demo: Demo_Multithreaded_Write_Event_Value.py
|
||||
|
||||
Here is that program for your inspection and education. It's SO nice to no longer poll for threaded events.
|
||||
|
||||
```python
|
||||
Demo_Threaded_Work.py - Best documented. Single thread used for long task
|
||||
Demo_Multithreaded_Long_Tasks.py - Similar to above, but with less fancy GUI. Allows you to set amount of time
|
||||
import threading
|
||||
import time
|
||||
import PySimpleGUI as sg
|
||||
|
||||
|
||||
"""
|
||||
Threaded Demo - Uses Window.write_event_value communications
|
||||
|
||||
Requires PySimpleGUI.py version 4.25.0 and later
|
||||
|
||||
This is a really important demo to understand if you're going to be using multithreading in PySimpleGUI.
|
||||
|
||||
Older mechanisms for multi-threading in PySimpleGUI relied on polling of a queue. The management of a communications
|
||||
queue is now performed internally to PySimpleGUI.
|
||||
|
||||
The importance of using the new window.write_event_value call cannot be emphasized enough. It will hav a HUGE impact, in
|
||||
a positive way, on your code to move to this mechanism as your code will simply "pend" waiting for an event rather than polling.
|
||||
|
||||
Copyright 2020 PySimpleGUI.org
|
||||
"""
|
||||
|
||||
THREAD_EVENT = '-THREAD-'
|
||||
|
||||
cp = sg.cprint
|
||||
|
||||
def the_thread(window):
|
||||
"""
|
||||
The thread that communicates with the application through the window's events.
|
||||
|
||||
Once a second wakes and sends a new event and associated value to the window
|
||||
"""
|
||||
i = 0
|
||||
while True:
|
||||
time.sleep(1)
|
||||
window.write_event_value('-THREAD-', (threading.current_thread().name, i)) # Data sent is a tuple of thread name and counter
|
||||
cp('This is cheating from the thread', c='white on green')
|
||||
i += 1
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
The demo will display in the multiline info about the event and values dictionary as it is being
|
||||
returned from window.read()
|
||||
Every time "Start" is clicked a new thread is started
|
||||
Try clicking "Dummy" to see that the window is active while the thread stuff is happening in the background
|
||||
"""
|
||||
|
||||
layout = [ [sg.Text('Output Area - cprint\'s route to here', font='Any 15')],
|
||||
[sg.Multiline(size=(65,20), key='-ML-', autoscroll=True, reroute_stdout=True, write_only=True, reroute_cprint=True)],
|
||||
[sg.T('Input so you can see data in your dictionary')],
|
||||
[sg.Input(key='-IN-', size=(30,1))],
|
||||
[sg.B('Start A Thread'), sg.B('Dummy'), sg.Button('Exit')] ]
|
||||
|
||||
window = sg.Window('Window Title', layout, finalize=True)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
cp(event, values)
|
||||
if event == sg.WIN_CLOSED or event == 'Exit':
|
||||
break
|
||||
if event.startswith('Start'):
|
||||
threading.Thread(target=the_thread, args=(window,), daemon=True).start()
|
||||
if event == THREAD_EVENT:
|
||||
cp(f'Data from the thread ', colors='white on purple', end='')
|
||||
cp(f'{values[THREAD_EVENT]}', colors='white on red')
|
||||
window.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
These 2 particular demos have a LOT of comments showing you where to add your code, etc.. The amount of code to do this is actually quite small and you don't need to understand the mechanisms used if you simply follow the demo that's been prepared for you.
|
||||
|
||||
### Multithreaded Programs
|
||||
|
||||
|
@ -1129,7 +1210,26 @@ theme_previewer
|
|||
|
||||
The first step is to create the window object using the desired window customizations.
|
||||
|
||||
Note - There is no direct support for "**modal windows**" in PySimpleGUI. All windows are accessible at all times unless you manually change the windows' settings.
|
||||
## Modal Windows (only applied to tkinter port currently
|
||||
)
|
||||
NOTE - as of PySimpleGUI 4.25.0 Modal Windows are supported! By default the `popup` windows that block will be marked Modal by default. This is a somewhat risky change because your expisting applications will behave differently. However, in theory, you shouldn't have been interacting with other windows while the popup is active. All of those actions are at best queued. It's implementation dependent.
|
||||
|
||||
"Modal" in this case means that you must close this "modal" window before you will be able to interact with windows created before this window. Think about an "about" box. You normally have to close this little popup in most programs. So, that's what PySimpleGUI is doing now.
|
||||
|
||||
## Making your window modal
|
||||
|
||||
To make a Modal Wio=ndow you have 2 options.
|
||||
|
||||
1. Set the `moodel=True` parameter in your Window calls.
|
||||
|
||||
2. Call the method `Window.make_modal()` to chance a window from non-modal to modal. There is no modal to non-modal. Don't see the need for one. If one comes up, sure!
|
||||
|
||||
|
||||
### Disabling modal windows
|
||||
|
||||
Popups that block are the only windows that have modal on by default. There is a modal parameter than you can set to False to turn it off.
|
||||
|
||||
For the earlier than 4.25.0 and other ports of PySimpleGUI There is no direct support for "**modal windows**" in PySimpleGUI. All windows are accessible at all times unless you manually change the windows' settings.
|
||||
|
||||
|
||||
**IMPORTANT** - Many of the `Window` methods require you to either call `Window.read` or `Window.Finalize` (or set `finalize=True` in your `Window` call) before you call the method. This is because these 2 calls are what actually creates the window using the underlying GUI Framework. Prior to one of those calls, the methods are likely to crash as they will not yet have their underlying widgets created.
|
||||
|
@ -1773,6 +1873,160 @@ Then to turn off return values for that element, the `Multiline` element would b
|
|||
sg.Multiline(size=(40,8), key='-MLINE-' + sg.WRITE_ONLY_KEY)
|
||||
```
|
||||
|
||||
## Key Errors - Key error recovery algorithm
|
||||
|
||||
In the primary (tkinter) port of PySimpleGUI, starting in version 4.27.0 (not yet on PyPI... but available on GitHub as 4.26.0.14+)
|
||||
|
||||
There are now 3 controls over key error handling and a whole new era of key reporting.
|
||||
|
||||
```python
|
||||
SUPPRESS_ERROR_POPUPS = False
|
||||
SUPPRESS_RAISE_KEY_ERRORS = False
|
||||
SUPPRESS_KEY_GUESSING = False
|
||||
```
|
||||
|
||||
You can modify these values by calling `set_options`.
|
||||
|
||||
```python
|
||||
sg.set_options(suppress_raise_key_errors=False, suppress_error_popups=False, suppress_key_guessing=False)
|
||||
```
|
||||
|
||||
A basic definition of them are:
|
||||
`suppress_error_popups` - Disables error popups that are generated within PySimpleGUI itself to not be shown
|
||||
`suppress_raise_key_errors` - Disables raising a key error if a key or a close match are not found
|
||||
`suppress_key_guessing` - Disables the key guessing algorithm should you have a key error
|
||||
|
||||
With the defaults left as defined (all `False`), here is how key errors work.
|
||||
|
||||
This is the program being used in this example:
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
def main():
|
||||
sg.set_options(suppress_raise_key_errors=False, suppress_error_popups=False, suppress_key_guessing=False)
|
||||
|
||||
layout = [ [sg.Text('My Window')],
|
||||
[sg.Input(k='-IN-'), sg.Text(size=(12,1), key='-OUT-')],
|
||||
[sg.Button('Go'), sg.Button('Exit')] ]
|
||||
|
||||
window = sg.Window('Window Title', layout, finalize=True)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.read()
|
||||
print(event, values)
|
||||
if event == sg.WIN_CLOSED or event == 'Exit':
|
||||
break
|
||||
window['-O U T'].update(values['-IN-'])
|
||||
window.close()
|
||||
|
||||
|
||||
def func():
|
||||
|
||||
main()
|
||||
|
||||
func()
|
||||
```
|
||||
|
||||
A few things to note about it:
|
||||
|
||||
* There are multiple levels of functions being called, not just a flat program
|
||||
* There are 2 keys explicitly defined, both are text at this point (we'll change them later)
|
||||
* There are 2 lookups happening, one with `window` the other with `values`
|
||||
|
||||
This key error recovery algorithm only applies to element keys being used to lookup keys inside of windows. The `values` key lookup is a plain dictionary and so nothing fancy is done for that lookup.
|
||||
|
||||
|
||||
### Example 1 - Simple text string misspelling
|
||||
|
||||
In our example, this line of code has an error:
|
||||
|
||||
```python
|
||||
window['-O U T'].update(values['-IN-'])
|
||||
```
|
||||
|
||||
There are multiple problems with the key `'-OUT-'`. It is missing a dash and it has a bunch of extra spaces.
|
||||
|
||||
When the program runs, you'll first see the layout with no apparent problems:
|
||||
|
||||

|
||||
|
||||
|
||||
Clicking the OK button will cause the program to return from `window.read()` and thus hit our bad key. The result will be a popup window that resembles this:
|
||||
|
||||

|
||||
|
||||
|
||||
Note a few things about this error popup. Your shown your bad key and you're also shown what you likely meant. Additionally, you're shown the filename, the line number and the line of code itself that has the error.
|
||||
|
||||
Because this error was recoverable, the program continues to run after you close the error popup. The result is what you expect from this program... the output field is the same as your information input.
|
||||
|
||||

|
||||
|
||||
|
||||
### Example 2 - Tuple error
|
||||
|
||||
Keys can be a variety of types, including tuples. In this particular program we have a tuple specified in the layout and have used an incorrect tuple in the lookup. Once again the recovery process worked and the program continued.
|
||||
|
||||

|
||||
|
||||
|
||||
### Example 3 - No close match found
|
||||
|
||||
In this example, as you can see in the error popup, there was such a mismatch that no substitution could be performed.
|
||||
|
||||

|
||||
|
||||
|
||||
This is an unrecoverable error, so a key error exception is raised.
|
||||
|
||||
```python
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 25, in <module>
|
||||
func()
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 23, in func
|
||||
main()
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 17, in main
|
||||
window[(1,2,3)].update(values['-IN-'])
|
||||
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 8591, in __getitem__
|
||||
return self.FindElement(key)
|
||||
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 7709, in FindElement
|
||||
raise KeyError(key)
|
||||
KeyError: (1, 2, 3)
|
||||
```
|
||||
|
||||
If you're running an IDE such as PyCharm, you can use the information from the assert to jump to the line of code in your IDE based on the crash data provided.
|
||||
|
||||
|
||||
### Choose Your Desired Combination
|
||||
|
||||
There are enough controls on this error handling that you can control how you want your program to fail. If you don't want any popups, and no guessing and would instead like to simply get an exception when the key error happens, then call `set_options` with this combination:
|
||||
|
||||
|
||||
```python
|
||||
sg.set_options(suppress_raise_key_errors=False, suppress_error_popups=True, suppress_key_guessing=True)
|
||||
```
|
||||
|
||||
This will cause Example #1 above to immediately get an exception when hitting the statement with the error. Even though the guessing is turned off, you're still provided with the closest match to help with your debugging....
|
||||
|
||||
```
|
||||
** Error looking up your element using the key: -O U T The closest matching key: -OUT-
|
||||
Traceback (most recent call last):
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 25, in <module>
|
||||
func()
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 23, in func
|
||||
main()
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_978 - key error example.py", line 17, in main
|
||||
window['-O U T'].update(values['-IN-'])
|
||||
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 8591, in __getitem__
|
||||
return self.FindElement(key)
|
||||
File "C:\Python\PycharmProjects\PSG\PySimpleGUI.py", line 7709, in FindElement
|
||||
raise KeyError(key)
|
||||
KeyError: '-O U T'
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Common Element Parameters
|
||||
|
@ -2660,7 +2914,6 @@ For example, this layout has a single slider element that spans several rows fol
|
|||
|
||||
Without a Column Element you can't create a layout like this. But with it, you should be able to closely match any layout created using tkinter only.
|
||||
|
||||
|
||||
```python
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
@ -2695,9 +2948,10 @@ sg.Popup(event, values, line_width=200)
|
|||
|
||||
```
|
||||
|
||||
### Column, Frame, Tab, Window element_justification
|
||||
|
||||
Beginning in Release 4.3 you can set the justification for any container element. This is done through the `element_justification` parameter. This will greatly help anyone that wants to center all of their content in a window. Previously it was difficult to do these kinds of layouts, if not impossible.
|
||||
## Columns As a Way to Modify Elements
|
||||
|
||||
Sometimes Columns are used to contain a single elemnet, but to give that elemously it was difficult to do these kinds of layouts, if not impossible.
|
||||
|
||||
justify the `Column` element's row by setting the `Column`'s `justification` parameter.
|
||||
|
||||
|
@ -3675,6 +3929,47 @@ You'll find the pattern - `window.Element(key)` in older code. All of code afte
|
|||
|
||||
Note that to change a progress meter's progress, you call `update_bar`, not `update`. A change to this is being considered for a future release.
|
||||
|
||||
# Cursors - Setting for Elements and Windows
|
||||
|
||||
It is possible to change the normal arrow cursor into something else by setting the cursor for an element or the entire window. The result will be the cursor changing when you move the mouse over the elements or Window.
|
||||
|
||||
One of the best examples is URLs. Users are accustomed to seeing a hand cursor when the mouse is moved over a link. By setting the cursor to a hand for a Text element that has text that is in the format of a URL, it signals to the user that it's a link that can be clicked.
|
||||
|
||||
The `set_cursor` method is used to set the cursor for an element. Perform an element look-up or use a variable containing an element, and call the `set_cursor` method, passing in a string that selects the cursor. The valid cursor names are documented in the tkinter docs as this call maps directly to a tkinter call.
|
||||
|
||||
These cursor strings were obtained from the Tk manual and are what you pass into the `set_cursor` methods.
|
||||
|
||||
## Windows Level Cursor
|
||||
|
||||
You can also set the cursor for the Window as a whole, including the margins and areas elements don't directly fill. Call `Window.set_cursor()` to set the cursor at the Window level.
|
||||
|
||||
## Valid Cursor Strings
|
||||
|
||||
`X_cursor, arrow, based_arrow_down, based_arrow_up, boat, bogosity, bottom_left_corner, bottom_right_corner, bottom_side, bottom_tee, box_spiral, center_ptr, circle, clock, coffee_mug, cross, cross_reverse, crosshair, diamond_cross, dot, dotbox, double_arrow, draft_large, draft_small, draped_box, exchange, fleur, gobbler, gumby, hand1, hand2, heart, icon, iron_cross, left_ptr, left_side, left_tee, leftbutton, ll_angle, lr_angle, man, middlebutton, mouse, pencil, pirate, plus, question_arrow, right_ptr, right_side, right_tee, rightbutton, rtl_logo, sailboat, sb_down_arrow, sb_h_double_arrow, sb_left_arrow, sb_right_arrow, sb_up_arrow, sb_v_double_arrow, shuttle, sizing, spider, spraycan, star, target, tcross, top_left_arrow, top_left_corner, top_right_corner, top_side, top_tee, trek, ul_angle, umbrella, ur_angle, watch, xterm`
|
||||
|
||||
|
||||
## No Cursor
|
||||
|
||||
To specify no cursor should be shown, the cursor `'no'` can be used on some platforms
|
||||
|
||||
## Windows OS Specific
|
||||
|
||||
One windows, these cursors map to native Windows cursors:
|
||||
|
||||
`arrow, center_ptr, crosshair, fleur, ibeam, icon, sb_h_double_arrow, sb_v_double_arrow, watch, xterm`
|
||||
|
||||
And these are also available:
|
||||
|
||||
`no, starting, size, size_ne_sw, size_ns, size_nw_se, size_we, uparrow, wait`
|
||||
|
||||
## Mac OS Specific
|
||||
|
||||
`arrow, cross, crosshair, ibeam, plus, watch, xterm`
|
||||
|
||||
And these additional native cursors are available for the Mac
|
||||
|
||||
`copyarrow, aliasarrow, contextualmenuarrow, text, cross-hair, closedhand, openhand, pointinghand, resizeleft, resizeright, resizeleftright, resizeup, resizedown, resizeupdown, none, notallowed, poof, countinguphand, countingdownhand, countingupanddownhand, spinning`
|
||||
|
||||
|
||||
# Keyboard & Mouse Capture
|
||||
|
||||
|
@ -4203,6 +4498,400 @@ Exception module 'tkinter' has no attribute '__version__'
|
|||
```
|
||||
---
|
||||
|
||||
# User Settings API
|
||||
|
||||
In release 4.30.0 there is a new set of API calls available to help with "user settings". Think of user settings as a dictionary that is automatically written to your hard drive. That's basically what it is. Underpinning the code is the JSON package provided by Python.
|
||||
|
||||
While using JSON files to save and load a settings dictionary isn't very difficult, it is still code you'll need to write if you want to save settings as part of your GUI. Since having "settings" for a GUI based program isn't uncommon, it made sense to build this capability into PySimpleGUI. Clearly you can still use your own method for saving settings, but if you're looking for a simple and easy way to do it, these calls are likely about as easy as it gets.
|
||||
|
||||
There have already been some demo programs written that use JSON files to store settings. You can expect that this capability will begin to show up in more demos in the future since it's now part of PySimpleGUI.
|
||||
|
||||
User settings are stored in a Python dictionary which is saved to / loaded from disk. Individual settings are thus keys into a dictionary. You do not need to explicitly read nor write the file. Changing any entry will cause the file to be saved. Reading any entry will cause the file to be read if it hasn't already been read.
|
||||
|
||||
## Two Interfaces
|
||||
|
||||
There are 2 ways to access User Settings
|
||||
|
||||
1. User Settings function calls
|
||||
2. The `UserSettings` class
|
||||
|
||||
They both offer the same basic operations. The class interface has an added benefit of being able to access the individual settings using the same syntax as Python dictionary.
|
||||
|
||||
|
||||
## List of Calls for Function Interface
|
||||
|
||||
|Function|Description|
|
||||
| --- | --- |
|
||||
|user_settings|Returns settings as a dictionary|
|
||||
|user_settings_delete_entry|Deletes a setting|
|
||||
|user_settings_delete_filename|Deletes the settings file|
|
||||
|user_settings_file_exists|Returns True if settings file specified exists|
|
||||
|user_settings_filename|Returns full path and filename of current settings file|
|
||||
|user_settings_get_entry|Returns value for a setting. If no setting found, then specified default value is returned|
|
||||
|user_settings_load|Loads dictionary from the settings file. This is not normally needed||
|
||||
|user_settings_save|Saves settings to current or newly specified file. Not normally needed|
|
||||
|user_settings_set_entry|Sets an entry to a particular value
|
||||
|user_settings_write_new_dictionary|Writes a specified dictionary to settings file|
|
||||
|
||||
|
||||
## Operations
|
||||
|
||||
There are 2 categories that the calls can be divided into.
|
||||
|
||||
1. File operations
|
||||
2. Settings operations
|
||||
|
||||
File operations involve working with the JSON file itself. They include:
|
||||
* Setting the path and/or filename
|
||||
* Load/save the file (these are somewhat optional as the saving loading/saving is done automatically)
|
||||
* Deleting the settings file
|
||||
* Checking if settings file exists
|
||||
|
||||
Generally speaking, a setting is specified with a key which is generally a string. Settings operations are for working with the individual settings and include:
|
||||
* Get the value of a setting (returns a default value if not found)
|
||||
* Set the value of a setting (also saves the settings to disk)
|
||||
|
||||
Any setting operation may cause the file to be written. This is because a "get" operation can include returning a default value if the setting isn't found. This means a new entry is made in your settings dictionary is one didn't exist before. Since a new entry is made, that means it needs to be also be written to disk.
|
||||
|
||||
## Filenames
|
||||
|
||||
The settings filename defaults the filename of your Python file making the call with the extension ".json" added. If your Python program is called `test.py` then your default settings filename will be `test.json`.
|
||||
|
||||
In addition to the filename having a default value, the path to the file also has a default value. The default depends on your operating system.
|
||||
|
||||
|Operating System|Default Path|
|
||||
| --- | --- |
|
||||
| Windows | \user\user_name\AppData\Local\PySimpleGUI\settings |
|
||||
| Linux | ~/.config/PySimpleGUI/settings |
|
||||
| Mac | ~/Library/Application Support/PySimpleGUI/settings |
|
||||
|
||||
When calling the User Settings APIs, if a parameter is named `filename`, you can specify a full path or just the filename. This will save you the trouble of having to split up your path and filename in your code. If you specify only the path, the the filename will be added to that path and named as defined earlier.
|
||||
|
||||
Like the rest of PySimpleGUI, the idea is for you to write as little code as possible. The default values for the filename and path should be fine for you to use. They will be stored in a location on your system that is meant to store user settings.
|
||||
|
||||
|
||||
### Setting Filename
|
||||
|
||||
If you want to see what the current filename is for your settings, then you can call `user_settings_filename()` with no parameters and you'll get back an absolute path and filename.
|
||||
|
||||
To make the code for specifying the folder and filename as simple as possible, the 2 parts are separated in the call specifying the name of the settings file. However, it is possible to supply a full and complete folder + filename as well.
|
||||
|
||||
The default filename for your settings file is the name of the file that makes the call to the User Settings API's with the `.py` extension changed to a `.json` extension. If your source file is called `demo.py`, then your settings filename will be `demo.json`.
|
||||
|
||||
#### Setting only the filename
|
||||
|
||||
If you want to control the name of the file and/or the path to the settings file, then you will use the `user_settings_filename` call. This function takes 2 parameters.
|
||||
|
||||
```python
|
||||
user_settings_filename(filename=None, path=None)
|
||||
```
|
||||
|
||||
If you set only the path, then the filename will default to the value already described. If you set only the filename, then the path will be the default path is dependent on your operating system. See the table above for the locations for each OS.
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
sg.user_settings_filename(filename='my_settings.json')
|
||||
print(sg.user_settings_filename())
|
||||
```
|
||||
|
||||
If you are running on Windows, then the result of running this code will be this printed on the console:
|
||||
|
||||
```
|
||||
C:\Users\your_use_name\AppData\Local\PySimpleGUI\settings\my_settings.json
|
||||
```
|
||||
|
||||
You are not restricted to naming your settings file to an extension of .json. That is simply the default extension that's used by PySimpleGUI. You can use any extension you would like, including no extension.
|
||||
|
||||
#### Setting only the path
|
||||
|
||||
Maybe you don't care about the settings filename itself, but you do care about where the settings are stored. Let's say you want the settings to be stored in the same folder as your Python source file. Specifying `path='.'` will achieve this.
|
||||
|
||||
|
||||
#### Setting a fully qualified filename
|
||||
|
||||
If you want to specify the full absolute path and filename of the settings file, you can do it by using the filename parameter. Instead of passing the filename only, pass in a fully qualified path and filename. If you want to name your settings file `a:\temp\my_settings`, then your call will look like this:
|
||||
|
||||
|
||||
```python
|
||||
sg.user_settings_filename(filename=r'a:\temp\my_settings')
|
||||
```
|
||||
|
||||
You are not required to break your file down into 2 parameters. You could if you wanted to however. The equivalent to the above call using 2 parameters would be:
|
||||
|
||||
```python
|
||||
sg.user_settings_filename(filename='my_settings' , path=r'a:\temp')
|
||||
```
|
||||
|
||||
|
||||
### Getting the current filename
|
||||
|
||||
Calling `user_settings_filename` with no parameters will return the full path and filename of your settings file as a single string.
|
||||
|
||||
|
||||
### File Loading / Saving
|
||||
|
||||
Generally speaking you will not need to load or save your settings file. It is automatically saved after every change.
|
||||
|
||||
Note that reading a setting can also cause the file to be written. If you read a setting and the setting did not exist, then your call to `user_settings_get_entry` will return the default value you specified. As a result, the dictionary is updated with this default value and in return the file is written with this value as well.
|
||||
|
||||
One of the situations where you may want to explicitly read/load the settings file is if you're expecting it to be modified by another program.
|
||||
|
||||
Like so much of PySimpleGUI, as much as possible is automatically done on your behalf. This includes the requirement of saving and loading your settings file. Even naming your settings file is optional.
|
||||
|
||||
## The `UserSettings` Class Interface
|
||||
|
||||
The `UserSettings` class makes working with settings look like a Python dictionary. The familiar [ ] syntax is used to read, write and delete entries.
|
||||
|
||||
### Creating a `UserSettings` Object
|
||||
|
||||
The first step is to create your setting object. The parameters are the same as calling the `user_settings_filename` function. If you want to use the default values, then leave the parameters unchanged.
|
||||
|
||||
```python
|
||||
settings = sg.UserSettings()
|
||||
```
|
||||
|
||||
This is the same as calling `sg.user_settings_filename()`
|
||||
|
||||
|
||||
### Reading, Writing, and Deleting an Individual Settings Using [ ] Syntax
|
||||
|
||||
The first operation will be to create the User Settings object.
|
||||
|
||||
```python
|
||||
settings = sg.UserSettings()
|
||||
```
|
||||
|
||||
To read a setting the dictionary-style [ ] syntax is used. If the item's name is `'-item-'`, then reading the value is achieved by writing
|
||||
|
||||
```python
|
||||
item_value = settings['-item-']
|
||||
```
|
||||
|
||||
Writing the setting is the same syntax except the expression is reversed.
|
||||
|
||||
```python
|
||||
settings['-item-'] = new_value
|
||||
```
|
||||
|
||||
To delete an item, again the dictionary style syntax is used.
|
||||
|
||||
```python
|
||||
del settings['-item-']
|
||||
```
|
||||
|
||||
You can also call the delete_entry method to delete the entry.
|
||||
|
||||
```python
|
||||
settings.delete_entry('-item-')
|
||||
```
|
||||
|
||||
### `UserSettings` Methods
|
||||
|
||||
You'll find all of the `UserSettings` methods available to you detailed in the Call Reference documentation.
|
||||
|
||||
One operation in particular that is not achievable using the [ ] notation is a "get" operation with a default value. For dictionaries, this method is `get` and for the `UserSettings` class the method is also called `get`. They both have an optional second parameter that represents a "default value" should the key not be found in the dictionary.
|
||||
|
||||
If you would like a setting with key `'-item-'` to return an empty string `''` instead of `None` if they key isn't found, then you can use this code to achieve that:
|
||||
|
||||
```python
|
||||
value = settings.get('-item-', '')
|
||||
```
|
||||
|
||||
It's the same kind of syntax that you're used to using with dictionaries.
|
||||
|
||||
|
||||
### Default Value
|
||||
|
||||
Normally the default value will be `None` if a key is not found and you get the value of the entry using the bracket format:
|
||||
|
||||
```python
|
||||
item_value = settings['-item-']
|
||||
```
|
||||
|
||||
You can change the default value by calling `settings.set_default_value(new_default)`. This will set the default value to return in the case when no key is found. Note that an exception is not raised when there is a key error (see next section on error handling). Instead, the default value is returned with a warning displayed.
|
||||
|
||||
|
||||
## Displaying the Settings Dictionary
|
||||
|
||||
The class interface makes it easy to dump out the dictionary. If you print the UserSettings object you'll get a printout of the dictionary.
|
||||
|
||||
Note that you'll need to "load" the settings from disk if you haven't performed any operations on the settings.
|
||||
|
||||
```python
|
||||
settings = sg.UserSettings()
|
||||
settings.load()
|
||||
print(settings)
|
||||
```
|
||||
|
||||
If you were to print the dictionary after creating the object, then the `load` is not needed
|
||||
|
||||
```python
|
||||
settings = sg.UserSettings()
|
||||
print(settings['-item-'])
|
||||
print(settings)
|
||||
```
|
||||
|
||||
To print the dictionary using the function call interface:
|
||||
|
||||
```python
|
||||
print(sg.user_settings())
|
||||
```
|
||||
|
||||
## Error Handling for User Settings
|
||||
|
||||
From a GUI perspective, user settings are not critical to the GUI operations itself. There is nothing about settings that will cause your window to not function. As a result, errors that occur in the User Settings are "soft errors". An error message is displayed along with information about how you called the function, when possible, and then execution continues.
|
||||
|
||||
One reason for treating these as soft errors and thus not raising an exception is that raising an exception will crash your GUI. If you have redirected your output, which many GUIs do, then you will see no error information and your window will simply disappear. If you double clicked a .py file to launch your GUI, both the GUI and the console window will instantly disappear if the GUI crashes, leaving you no information to help you debug the problem.
|
||||
|
||||
The only time errors can occur are during file operations. Typically these errors happen because you've specified a bad path or you don't have write permission for the path you specified.
|
||||
|
||||
Example error message. If you executed this code:
|
||||
|
||||
```python
|
||||
def main():
|
||||
sg.user_settings_filename(path='...')
|
||||
sg.user_settings_set_entry('-test-',123)
|
||||
```
|
||||
|
||||
Then you'll get an error when trying to set the '-test-' entry because `'...'` is not a valid path.
|
||||
|
||||
```
|
||||
*** Error saving settings to file:***
|
||||
...\scratch_1065.json [Errno 2] No such file or directory: '...\\scratch_1065.json'
|
||||
The PySimpleGUI internal reporting function is save
|
||||
The error originated from:
|
||||
File "C:/Users/mike/.PyCharmCE2019.1/config/scratches/scratch_1065.py"
|
||||
line 8
|
||||
in main
|
||||
sg.user_settings_set_entry('-test-',123)
|
||||
```
|
||||
|
||||
You should be able to easily figure out these errors as they are file operations and the error messages are clear in detailing what's happened and where the call originated.
|
||||
|
||||
### Silenting the Errors
|
||||
|
||||
If you're the type that doesn't want to see any error messages printed out on your console, then you can silence the error output.
|
||||
|
||||
When using the class interface, there is a parameter `silent_on_error` that you can set to `True`.
|
||||
|
||||
For the function interface, call the function `user_settings_silent_on_error()` and set the parameter to `True`
|
||||
|
||||
|
||||
## Coding Convention for User Settings Keys
|
||||
|
||||
The User Settings prompted a new coding convention that's been added to PySimpleGUI examples. As you're likely aware, keys in layouts have the format `'-KEY-`'. For UserSettings, a similar format is used, but instead of the string being in all upper case, the characters are lower case. In the example below, the user setting for "filename" has a User Setting key of `'-filename-'`. Coding conventions are a good thing to have in your projects. You don't have to follow this one of course, but you're urged to create your own for places in your code that it makes sense. You could say that PEP8 is one giant coding convention for the Python language as a whole. You don't have to follow it, but most Python programmers do. We follow it "by convention".
|
||||
|
||||
The reason this is done in PySimpleGUI is so that the keys are immediately recognizable. Perhaps your application has dictionaries that you use. If you follow the PySimpleGUI coding convention of Element keys have the format `'-KEY-'` and User Settings keys have the format of `'-key-'`, then you'll immediately understand what a specific key is used for. Your company may have its own coding conventions so follow those if appropriate instead of what you see in the PySimpleGUI examples.
|
||||
|
||||
## Example User Settings Usage
|
||||
|
||||
One of the primary places settings are likely to be used is for filenames / folder names. How many times have you run the same program and needed to enter the same filename? Even if the name of the file is on your clipboard, it's still a pain in the ass to paste it into the input field every time you run the code. Wouldn't it be so much simpler if your program remembered the last value you entered? Well, that's exactly why this set of APIs was developed.... again it was from laziness that this capability gained life.
|
||||
|
||||
If you want your `Input` elements to default to an entry from your settings, then you simply set the first parameter (`default_text`) to the value of a setting from your settings file.
|
||||
|
||||
Let's say your layout had this typical file input row:
|
||||
|
||||
```python
|
||||
[sg.Input(key='-IN-'), sg.FileBrowse()]
|
||||
```
|
||||
|
||||
To automatically fill in the `Input` to be the last value entered, use this layout row:
|
||||
|
||||
```python
|
||||
[sg.Input(sg.user_settings_get_entry('-filename-', ''), key='-IN-'), sg.FileBrowse()]
|
||||
```
|
||||
|
||||
When your user clicks OK or closes the window in a way that is in a positive way (instead of cancelling), then add this statement to save the value.
|
||||
|
||||
```python
|
||||
sg.user_settings_set_entry('-filename-', values['-IN-'])
|
||||
```
|
||||
|
||||
Here's an entire program demonstrating this way of using user settings
|
||||
|
||||

|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[sg.Text('Enter a filename:')],
|
||||
[sg.Input(sg.user_settings_get_entry('-filename-', ''), key='-IN-'), sg.FileBrowse()],
|
||||
[sg.B('Save'), sg.B('Exit Without Saving', key='Exit')]]
|
||||
|
||||
window = sg.Window('Filename Example', layout)
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
if event in (sg.WINDOW_CLOSED, 'Exit'):
|
||||
break
|
||||
elif event == 'Save':
|
||||
sg.user_settings_set_entry('-filename-', values['-IN-'])
|
||||
|
||||
window.close()
|
||||
```
|
||||
|
||||
In 2 lines of code you've just made life for your user so much easier. And, by not specifying a location and name for your file, the settings are stored out of sight / out of mind. If you wanted to have the settings be stored with your program file so that it's more visible, then add this statement before your layout:
|
||||
|
||||
```python
|
||||
sg.user_settings_filename(path='.')
|
||||
```
|
||||
|
||||
## Example Using UserSettings Class with [ ] Syntax
|
||||
|
||||
The same example can be written using the `UserSettings` class and the [ ] lookup syntax.
|
||||
|
||||
Here's the same program as above.
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
settings = sg.UserSettings()
|
||||
|
||||
layout = [[sg.Text('Enter a filename:')],
|
||||
[sg.Input(settings.get('-filename-', ''), key='-IN-'), sg.FileBrowse()],
|
||||
[sg.B('Save'), sg.B('Exit Without Saving', key='Exit')]]
|
||||
|
||||
window = sg.Window('Filename Example', layout)
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
if event in (sg.WINDOW_CLOSED, 'Exit'):
|
||||
break
|
||||
elif event == 'Save':
|
||||
settings['-filename-'] = values['-IN-']
|
||||
|
||||
window.close()
|
||||
```
|
||||
|
||||
If you were to place these 2 examples in the same file so that one ran after the other, you will find that the same settings file is used and thus the value saved in the first example will be read by the second one.
|
||||
|
||||
There was one additional line of code added:
|
||||
|
||||
```python
|
||||
settings.set_default_value('') # Set the default not-found value to ''
|
||||
|
||||
```
|
||||
|
||||
Strictly speaking, this line isn't needed because the Input Element now takes `None` to be the same as a value of `''`, but to produce identical results I added this line of code.
|
||||
|
||||
|
||||
## Demo Programs
|
||||
|
||||
There are a number of demo programs that show how to use UserSettings to create a richer experience for your users by remember the last value input into input elements or by adding a Combobox with a history of previously entered values. These upgrades make for a much easier to use GUI, especially when you find yourself typing in the same values or using the same files/folders.
|
||||
|
||||
|
||||
## Brief Caution - User Settings Stick Around
|
||||
|
||||
If you're using the default path, remember that previous runs of your file may have old settings that are still in your settings file. It can get confusing when you've forgotten that you previously wrote a setting. Not seeing the filename can have drawbacks like this.
|
||||
|
||||
Also, because the settings automatically save after every update, it can be easy to accidently overwrite a previously saved setting. If you want to avoid this, then perhaps it's best that you work with a dictionary within your code and then explicitly save your dictionary when you're ready to commit it to disk.
|
||||
|
||||
To save your Python dictionary to a settings file, simply call `user_settings_write_new_dictionary(dict)`, passing in your dictionary as the parameter.
|
||||
|
||||
|
||||
-------------------------
|
||||
|
||||
|
||||
# Extending PySimpleGUI
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue