Merge pull request #1149 from PySimpleGUI/Dev-latest

Dev latest
This commit is contained in:
MikeTheWatchGuy 2019-02-08 16:08:44 -05:00 committed by GitHub
commit c7a6db986e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 186 deletions

View File

@ -442,7 +442,7 @@ class Element():
if visible is False:
widget.attributes['hidden'] = 'true'
elif visible is True:
widget.attributes['hidden'] = 'false'
del(widget.attributes['hidden'])
def __del__(self):
pass
@ -1482,14 +1482,7 @@ class Button(Element):
self.ParentForm.FormRemainedOpen = True
element_callback_quit_mainloop(self)
elif self.BType == BUTTON_TYPE_CLOSES_WIN_ONLY: # special kind of button that does not exit main loop
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = self.ButtonText
if self.ParentForm.CurrentlyRunningMainloop: # if this window is running the mainloop, kick out
self.ParentForm.App.ExitMainLoop()
self.ParentForm.IgnoreClose = True
self.ParentForm.MasterFrame.Close()
element_callback_quit_mainloop(self)
self.ParentForm._Close()
Window.DecrementOpenCount()
elif self.BType == BUTTON_TYPE_CALENDAR_CHOOSER: # this is a return type button so GET RESULTS and destroy window
@ -2592,6 +2585,8 @@ class Window:
stdout_is_rerouted = False
stdout_location = None
port_number = 6900
active_windows = [ ] # type: Window []
App = None
def __init__(self, title, default_element_size=DEFAULT_ELEMENT_SIZE, default_button_element_size=(None, None),
auto_size_text=None, auto_size_buttons=None, location=(None, None), size=(None, None),
@ -2693,6 +2688,7 @@ class Window:
self.thread_id = None
self.App = None # type: Window.MyApp
self.MessageQueue = Queue()
self.master_widget = None
@classmethod
def IncrementOpenCount(self):
@ -3028,25 +3024,31 @@ class Window:
self.MasterFrame.Maximize()
def _Close(self):
try:
self.TKroot.update()
except:
pass
if not self.NonBlocking:
BuildResults(self, False, self)
if self.TKrootDestroyed:
return None
self.TKrootDestroyed = True
self.RootNeedsDestroying = True
self.Close()
self.__del__()
return None
def Close(self):
if len(Window.active_windows) != 0:
del(Window.active_windows[-1]) # delete current window from active windows
if len(Window.active_windows) != 0:
window = Window.active_windows[-1] # get prior window to change to
print(f'In close, changing to widget {window.master_widget}')
Window.App.set_root_widget(window.master_widget)
else:
self.App.close()
self.App.server.server_starter_instance._alive = False
self.App.server.server_starter_instance._sserver.shutdown()
return
self.App.close()
self.App.server.server_starter_instance._alive = False
self.App.server.server_starter_instance._sserver.shutdown()
if self.TKrootDestroyed:
return
# try:
# self.MasterFrame.Close()
# except:
@ -3183,23 +3185,28 @@ class Window:
class MyApp(remi.App):
def __init__(self,*args):
def __init__(self,*args, userdata2=None):
# self.window = window # type: Window
# print(args[-1])
userdata = args[-1].userdata
self.window = userdata[0] # type: Window
if userdata2 is None:
userdata = args[-1].userdata
self.window = userdata[0] # type: Window
else:
self.window = userdata2
self.window.App = self
super(Window.MyApp, self).__init__(*args)
self.master_widget = None
if userdata2 is None:
super(Window.MyApp, self).__init__(*args)
def main(self, name='world'):
# margin 0px auto allows to center the app to the screen
wid = remi.gui.VBox()
wid.style['justify-content'] = 'flex-start'
wid.style['align-items'] = 'baseline'
self.master_widget = remi.gui.VBox()
self.master_widget.style['justify-content'] = 'flex-start'
self.master_widget.style['align-items'] = 'baseline'
if self.window.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
wid.style['background-color'] = self.window.BackgroundColor
self.master_widget.style['background-color'] = self.window.BackgroundColor
try:
PackFormIntoFrame(self.window, wid, self.window)
PackFormIntoFrame(self.window, self.master_widget, self.window)
except:
print('* ERROR PACKING FORM *')
print(traceback.format_exc())
@ -3208,14 +3215,15 @@ class Window:
tag = remi.gui.Tag(_type='script')
tag.add_child("javascript", """window.onunload=function(e){sendCallback('%s','%s');return "close?";};""" % (
str(id(self)), "on_window_close"))
wid.add_child("onunloadevent", tag)
self.master_widget.add_child("onunloadevent", tag)
self.window.MessageQueue.put('Layout complete') # signal the main code that the layout is all done
return wid # returning the root widget
self.window.master_widget = self.master_widget
return self.master_widget # returning the root widget
def on_window_close(self):
# here you can handle the unload
# print("app closing")
print("app closing")
self.close()
self.server.server_starter_instance._alive = False
self.server.server_starter_instance._sserver.shutdown()
@ -4821,80 +4829,28 @@ def StartupTK(window:Window):
master = 00000
InitializeResults(window)
# REMI - this call moved to inside of the REMI thread
# PackFormIntoFrame(window, master, window)
# ....................................... DONE creating and laying out window ..........................#
# if MyFlexForm._Size != (None, None):
# master.geometry("%sx%s" % (MyFlexForm._Size[0], MyFlexForm._Size[1]))
# Center in middle of screen
# screen_width = master.winfo_screenwidth() # get window info to move to middle of screen
# screen_height = master.winfo_screenheight()
# if window.Location != (None, None):
# x, y = window.Location
# elif DEFAULT_WINDOW_LOCATION != (None, None):
# x, y = DEFAULT_WINDOW_LOCATION
# else:
# master.update_idletasks() # don't forget to do updates or values are bad
# win_width = master.winfo_width()
# win_height = master.winfo_height()
# x = screen_width / 2 - win_width / 2
# y = screen_height / 2 - win_height / 2
# if y + win_height > screen_height:
# y = screen_height - win_height
# if x + win_width > screen_width:
# x = screen_width - win_width
#
# move_string = '+%i+%i' % (int(x), int(y))
# master.geometry(move_string)
#
# master.update_idletasks() # don't forget
if len(Window.active_windows) == 0:
window.thread_id = threading.Thread(target=window.remi_thread, daemon=True)
window.thread_id.daemon = True
window.thread_id.start()
item = window.MessageQueue.get() # Get the layout complete message
Window.active_windows.append(window)
Window.App = window.App
else:
# margin 0px auto allows to center the app to the screen
master_widget = remi.gui.VBox()
master_widget.style['justify-content'] = 'flex-start'
master_widget.style['align-items'] = 'baseline'
if window.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
master_widget.style['background-color'] = window.BackgroundColor
PackFormIntoFrame(window, master_widget, window)
window.master_widget = master_widget
Window.active_windows.append(window)
Window.App.set_root_widget(master_widget)
window.thread_id = threading.Thread(target=window.remi_thread, daemon=True)
window.thread_id.daemon = True
window.thread_id.start()
item = window.MessageQueue.get() # Get the layout complete message
# my_flex_form.SetIcon(my_flex_form.WindowIcon)
# root.attributes('-alpha', my_flex_form.AlphaChannel) # Make window visible again
# if my_flex_form.ReturnKeyboardEvents and not my_flex_form.NonBlocking:
# root.bind("<KeyRelease>", my_flex_form._KeyboardCallback)
# root.bind("<MouseWheel>", my_flex_form._MouseWheelCallback)
# elif my_flex_form.ReturnKeyboardEvents:
# root.bind("<Key>", my_flex_form._KeyboardCallback)
# root.bind("<MouseWheel>", my_flex_form._MouseWheelCallback)
# if my_flex_form.AutoClose:
# duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form.AutoCloseDuration is None else my_flex_form.AutoCloseDuration
# my_flex_form.TKAfterID = root.after(duration * 1000, my_flex_form._AutoCloseAlarmCallback)
#
# if my_flex_form.Timeout != None:
# my_flex_form.TKAfterID = root.after(my_flex_form.Timeout, my_flex_form._TimeoutAlarmCallback)
# if my_flex_form.NonBlocking:
# my_flex_form.TKroot.protocol("WM_DESTROY_WINDOW", my_flex_form.OnClosingCallback)
# my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback)
# else: # it's a blocking form
# print('..... CALLING MainLoop')
# my_flex_form.CurrentlyRunningMainloop = True
# my_flex_form.TKroot.protocol("WM_DESTROY_WINDOW", my_flex_form.OnClosingCallback)
# my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback)
# my_flex_form.TKroot.mainloop()
# my_flex_form.CurrentlyRunningMainloop = False
# my_flex_form.TimerCancelled = True
# print('..... BACK from MainLoop')
# if not my_flex_form.FormRemainedOpen:
# _my_windows.Decrement()
# if my_flex_form.RootNeedsDestroying:
# my_flex_form.TKroot.destroy()
# my_flex_form.RootNeedsDestroying = False
return
# ==============================_GetNumLinesNeeded ==#
# Helper function for determining how to wrap text #
# ===================================================#
@ -5937,7 +5893,7 @@ def Popup(*args, button_color=None, background_color=None, text_color=None, butt
button, values = window.Read(timeout=0)
else:
button, values = window.Read()
window.Close()
window.Close()
return button
@ -6469,7 +6425,7 @@ def main():
[Text('PySimpleGUIWeb Welcomes You...', tooltip='text', font=('Comic sans ms', 20),size=(40,1), text_color='red', enable_events=True, key='_PySimpleGUIWeb_')],
[T('Current Time '), Text('Text', key='_TEXT_', font='Arial 18', text_color='black', size=(30,1)), Column(col1, background_color='red')],
[T('Up Time'), Text('Text', key='_TEXT_UPTIME_', font='Arial 18', text_color='black', size=(30,1))],
[Input('Single Line Input', do_not_clear=True, enable_events=True, size=(30, 1), text_color='red')],
[Input('Single Line Input', do_not_clear=True, enable_events=False, size=(30, 1), text_color='red')],
[Multiline('Multiline Input', do_not_clear=True, size=(40, 4), enable_events=True, key='_MULTI_IN_')],
[MultilineOutput('Multiline Output', size=(80, 8), text_color='blue', font='Courier 12', key='_MULTIOUT_')],
[Checkbox('Checkbox 1', enable_events=True, key='_CB1_'), Checkbox('Checkbox 2', default=True, key='_CB2_', enable_events=True)],
@ -6478,7 +6434,7 @@ def main():
[Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), enable_events =True, size=(10, 3), key='_LIST_')],
[Slider((1, 100), default_value=80, key='_SLIDER_', visible=True, enable_events=True)],
[Spin(values=(1, 2, 3), initial_value='2', size=(4, 1), key='_SPIN_', enable_events=True)],
[OK(), Button('Hidden', visible=False), Button('Values'), Button('Exit', button_color=('white', 'red'))]
[OK(), Button('Hidden', visible=False, key='_HIDDEN_'), Button('Values'), Button('Exit', button_color=('white', 'red')), Button('UnHide')]
]
window = Window('PySimpleGUIWeb Window', font='Arial 18',default_element_size=(12,1), auto_size_buttons=False).Layout(layout)
@ -6499,6 +6455,10 @@ def main():
window.Element('_MULTIOUT_').Update(str(values), append=True)
elif event != TIMEOUT_KEY:
window.Element('_MULTIOUT_').Update('EVENT: ' + str(event), append=True)
if event == 'UnHide':
print('Unhiding...')
window.Element('_HIDDEN_').Update(visible=True)
window.Close()

View File

@ -17,10 +17,7 @@
![Awesome Meter](https://img.shields.io/badge/Awesome_meter-100-yellow.svg)
![Python Version](https://img.shields.io/badge/Python-2.7_3.x-yellow.svg)
# PySimpleGUI
@ -33,15 +30,15 @@
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.241.0-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.24.0-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.241.0-blue.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.24.0-blue.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.22.0-orange.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUIQt_Version-0.22.0-orange.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUIWx_version-0.3.0-orange.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUIWebFor_Python_3.x_Version-0.2.23.0-orange.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_Version-0.2.2-orange.svg?longCache=true&style=for-the-badge)
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -70,7 +67,6 @@ As of 9/25/2018 **both Python 3 and Python 2.7 are supported**! The Python 3 v
# Qt Version
### Github
Check out the new PySimpleGUI port to the Qt GUI Framework. You can learn more on the [PySimpleGUIQt GitHub site](https://github.com/MikeTheWatchGuy/PySimpleGUI/tree/master/PySimpleGUIQt). **There is a separate Readme file for the Qt version** that you'll find there.
@ -118,12 +114,11 @@ Educators in particular should be interested. Students can not only post their
Depending on how you're viewing this document, you may or may not see an embedded browser window below that is running PySimpleGUI code.
<iframe height="400px" width="100%" src="https://repl.it/@PySimpleGUI/PySimpleGUIWeb-Demos?lite=true" scrolling="no" frameborder="no" allowtransparency="true" allowfullscreen="true" sandbox="allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-modals"></iframe>
<iframe height="1000px" width="100%" src="https://repl.it/@PySimpleGUI/PySimpleGUIWeb-Demos?lite=true" scrolling="no" frameborder="no" allowtransparency="true" allowfullscreen="true" sandbox="allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-modals"></iframe>
# Support
PySimpleGUI is an active project. Bugs are fixed, features are added, often. Should you run into trouble, open an issue on the GitHub site and you'll receive help by someone in the community. It's very early in the port however with MANY more Elements left to implement. 3 down, 20-something to go.
PySimpleGUI is an active project. Bugs are fixed, features are added, often. Should you run into trouble, open an issue on the GitHub site and you'll receive help by someone in the community.
# Platforms
@ -196,9 +191,7 @@ In addition to a primary GUI, you can add a Progress Meter to your code with ONE
It's simple to show animated GIFs.
![loading animationYou can build an async media player GUI with custom buttons in 30 lines of code.
![media player 2](https://user-images.githubusercontent.com/13696193/51280871-d2041e80-19ae44960091-eeebf980-aec6-11e98-8757-802eb95352ed.gif84e-80d4447a83cd.jpg)
![loading animation](https://user-images.githubusercontent.com/13696193/51280871-d2041e80-19ae-11e9-8757-802eb95352ed.gif)
How about embedding a game inside of a GUI? This game of Pong is written in tkinter and then dropped into the PySimpleGUI window creating a game that has an accompanying GUI.
@ -469,12 +462,6 @@ PySimpleGUI Runs on all Python3 platforms that have tkinter running on them. It
If you wish to create an EXE from your PySimpleGUI application, you will need to install `PyInstaller`. There are instructions on how to create an EXE at the bottom of this ReadMe
## Qt Version
Please see the Qt specific documentation. The readme that contains all you need to know about Qt specific matters is here:
https://pysimplegui.readthedocs.io/readmeqt/
## Using - Python 3
To use in your code, simply import....
@ -822,9 +809,7 @@ time_between_frames - amount of time in milliseconds to use between frames
***To close animated popups***, call PopupAnimated with `image_source=None`. This will close all of the currently open PopupAnimated windows.
# Progress Meters!
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?
@ -1637,7 +1622,7 @@ Populates the windows fields with the values shown in the dictionary.
#### Element(key, silent_on_error=False) (shorthand version)
#### FindElement(key, silent_on_error=False)
Rerturns the Element that has a matching key. If the key is not found, an Error Element is returned so that the program will not crash should the user try to perform an "update". A Popup message will be shown
Returns the Element that has a matching key. If the key is not found, an Error Element is returned so that the program will not crash should the user try to perform an "update". A Popup message will be shown
#### FindElementWithFocus()
@ -2423,9 +2408,9 @@ Parameter definitions
visible - if False will create image as hidden
size_px - size of image in pixels
### `Update`Image Methods
### `Update` Method
Like other Elements, the Image Element has an `Uupdate` method. Call Update if you want to change the image.
Like other Elements, the Image Element has an `Update` method. Call Update if you want to change the image.
def Update(self, filename=None, data=None, visible=None):
@ -2450,7 +2435,7 @@ UpdateAnimation(source,
You can call the method without setting the `time_between_frames` value and it will show a frame and immediately move on to the next frame. This enables you to do the inter-frame timing.
## Button Element
@ -3300,7 +3285,7 @@ Unlike Tables there is no standard format for trees. Thus the data structure pa
def TreeData()
def Insert(self, parent, key, text, values, icon=None)
```
To "insert" data into the tree the TreeData method Insert is called.
`Insert(parent_key, key, display_text, values)`
@ -4286,55 +4271,6 @@ There are too many to list!!
There are over 130 sample programs to give you a jump start.
Use the example programs as a starting basis for your GUI. Copy, paste, modify and run! The demo files are:
| Source File| Description |
|--|--|
|**Demo_All_Widgets.py**| Nearly all of the Elements shown in a single window
|**Demo_Borderless_Window.py**| Create clean looking windows with no border
|**Demo_Button_States.py**| One way of implementing disabling of buttons
|**Demo_Calendar.py** | Demo of the Calendar Chooser button
|**Demo_Canvas.py** | window with a Canvas Element that is updated outside of the window
|**Demo_Chat.py** | A chat window with scrollable history
|**Demo_Chatterbot.py** | Front-end to Chatterbot Machine Learning project
|**Demo_Color.py** | How to interact with color using RGB hex values and named colors
|**Demo_Columns.py** | Using the Column Element to create more complex windows
|**Demo_Compare_Files.py** | Using a simple GUI front-end to create a compare 2-files utility
|**Demo_Cookbook_Browser.py** | Source code browser for all Recipes in Cookbook
|**Demo_Dictionary.py** | Specifying and using return values in dictionary format
**Demo_DOC_Viewer_PIL.py** | Display a PDF, HTML, ebook file, etc in your window
|**Demo_DisplayHash1and256.py** | Using high level API and custom window to implement a simple display hash code utility
|**Demo_DuplicateFileFinder.py** | High level API used to get a folder that is used by utility that finds duplicate files. Uses progress meter to show progress. 2 lines of code required to add GUI and meter
|**Demo_Fill_Form.py** | How to perform a bulk-fill for a window. Saving and loading a window from disk
|**Demo Font Sizer.py** | Demonstrates Elements updating other Elements
|**Demo_Func_Callback_Simulator.py** | For the Raspberry Pi crowd. Event loop that simulates traditional GUI callback functions should you already have an architecture that uses them
|**Demo_GoodColors.py** | Using some of the pre-defined PySimpleGUI individual colors
|**Demo_HowDoI.py** | This is a utility to be experienced! It will change how you code
|**Demo_Img_Viewer.py** | Display jpg, png,tiff, bmp files
|**Demo_Keyboard.py** | Using blocking keyboard events
|**Demo_Keyboard_Realtime.py** | Using non-blocking / realtime keyboard events
|**Demo_Machine_Learning.py** | A sample Machine Learning front end
|**Demo_Matplotlib.py** | Integrating with Matplotlib to create a single graph
|**Demo_Matplotlib_Animated.py** | Animated Matplotlib line graph
|**Demo_Matplotlib_Animated_Scatter.py** | Animated Matplotlib scatter graph
|**Demo_Matplotlib_Browser.py** | Browse Matplotlib gallery
|**Demo_Media_Player.py** | Non-blocking window with a media player layout. Demonstrates button graphics, Update method
|**Demo_MIDI_Player.py** | GUI wrapper for Mido MIDI package. Functional MIDI player that controls attached MIDI devices
|**Demo_NonBlocking_Form.py** | a basic async window
|**Demo_OpenCV.py** | Integrated with OpenCV
|**Demo_Password_Login** | Password protection using SHA1
|**Demo_PDF_Viewer.py** | Submitted by a user! Previews PDF documents. Uses keyboard input & mouse scrollwheel to navigate
|**Demo_Pi_LEDs.py** | Control GPIO using buttons
|**Demo_Pi_Robotics.py** | Simulated robot control using realtime buttons
|**Demo_PNG_Vierwer.py** | Uses Image Element to display PNG files
| **Demo_Progress_Meters.py** | Demonstrates using 2 progress meters simultaneously
|**Demo_Recipes.py** | A collection of various Recipes. Note these are not the same as the Recipes in the Recipe Cookbook
|**Demo_Script_Launcher.py** | Demonstrates one way of adding a front-end onto several command line scripts
|**Demo_Script_Parameters.py** | Add a 1-line GUI to the front of your previously command-line only scripts
|**Demo_Tabbed_Form.py** | Using the Tab feature
|**Demo_Table_Simulation.py** | Use input fields to display and edit tables
|**Demo_Timer.py** | Simple non-blocking window
## Packages Used In Demos
@ -4540,7 +4476,7 @@ Listboxes are still without scrollwheels. The mouse can drag to see more items.
3.0 We've come a long way baby! Time for a major revision bump. One reason is that the numbers started to confuse people the latest release was 2.30, but some people read it as 2.3 and thought it went backwards. I kinda messed up the 2.x series of numbers, so why not start with a clean slate. A lot has happened anyway so it's well earned.
One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn this off for windows with sliders. This feature is enabled in the Window call.
One change that will set PySimpleGUI apart is the parlor trick of being able to move the window by clicking on it anywhere. This is turned on by default. It's not a common way to interact with windows. Normally you have to move using the titlebar. Not so with PySimpleGUI. Now you can drag using any part of the window. You will want to turn off for windows with sliders. This feature is enabled in the Window call.
Related to the Grab Anywhere feature is the no_titlebar option, again found in the call to Window. Your window will be a spiffy, borderless window. It's a really interesting effect. Slight problem is that you do not have an icon on the taskbar with these types of windows, so if you don't supply a button to close the window, there's no way to close it other than task manager.
@ -4890,7 +4826,7 @@ Emergency patch release... going out same day as previous release
## 3.20.0 & 1.20.0 18-Dec-2018
* New Pane Element
* Graphh.DeleteFigure method
* Graph.DeleteFigure method
* disable_minimize - New parameter for Window
* Fix for 2.7 menus
* Debug Window no longer re-routes stdout by default
@ -4996,11 +4932,67 @@ You can also look up elements using their keys. This is an excellent way to upd
This is a language feature that is featured **heavily** in all of the API calls, both functions and classes. Elements are configured, in-place, by setting one or more optional parameters. For example, a Text element's color is chosen by setting the optional `text_color` parameter.
**tkinter**
tkinter is the "official" GUI that Python supports. It runs on Win
tkinter is the "official" GUI that Python supports. It runs on Windows, Linux, and Mac. It was chosen as the first target GUI framework due to its ***ubiquity***. Nearly all Python installations, with the exception of Ubuntu Linux, come pre-loaded with tkinter. It is the "simplest" of the GUI frameworks to get up an running (among Qt, WxPython, Kivy, etc).
From the start of the PSG project, tkinter was not meant to be the only underlying GUI framework for PySimpleGUI. It is merely a starting point. All journeys begin with one step forward and choosing tkinter was the first of many steps for PySimpleGUI.
## Author
MikeB
## Demo Code Contributors
[JorjMcKie](https://github.com/JorjMcKie) - PDF and image viewers (plus a number of code suggestions)
[Otherion](https://github.com/Otherion) - Table Demos Panda & CSV. Loads of suggestions to the core APIs
## License
GNU Lesser General Public License (LGPL 3) +
## Acknowledgments
* [JorjMcKie](https://github.com/JorjMcKie) was the motivator behind the entire project. His wxsimpleGUI concepts sparked PySimpleGUI into existence
* [Fredrik Lundh](https://wiki.python.org/moin/FredrikLundh) for his work on `tkinter`
* [Ruud van der Ham](https://forum.pythonistacafe.com/u/Ruud) for all the help he's provided as a Python-mentor. Quite a few tricky bits of logic was supplied by Ruud. The dual-purpose return values scheme is Ruud's for example
* **Numerous** users who provided feature suggestions! Many of the cool features were suggested by others. If you were one of them and are willing to take more credit, I'll list you here if you give me permission. Most are too modest
* [moshekaplan](https://github.com/moshekaplan)/**[tkinter_components](https://github.com/moshekaplan/tkinter_components)** wrote the code for the Calendar Chooser Element. It was lifted straight from GitHub
* [Bryan Oakley](https://stackoverflow.com/users/7432/bryan-oakley) for the code that enables the `grab_anywhere` feature.
* [Otherion](https://github.com/Otherion) for help with Tables, being a sounding board for new features, naming functions, ..., all around great help
* [agjunyent](https://github.com/agjunyent) figured out how to properly make tabs and wrote prototype code that demonstrated how to do it
* [jfongattw](https://github.com/jfongattw) huge suggestion... dictionaries. turned out to be
* one of the most critical constructs in PySimpleGUI
* [venim](https://github.com/venim) code to doing Alt-Selections in menus, updating Combobox using index, request to disable windows (a really good idea), checkbox and tab submits on change, returning keys for elements that have change_submits set, ...
* [rtrrtr](https://github.com/rtrrtr) Helped get the 2.7 and 3.x code unified (big damned deal)
* Tony Crewe (anthony.crewe@gmail.com) Generously provided his classroom materials that he has written to teach a GUI course. If you're an educator and want to trade materials with Tony, he would like to hear from you.
* [spectre6000](https://github.com/spectre6000) - Readme updates
* [jackyOO7](https://github.com/jackyOO7) - Demo programs. OpenCV with realtime image processing, popup keyboard, input Combo read only option.
* [AltoRetrato](https://github.com/AltoRetrato) - Fonts for multiline and combo
* [frakman1](https://github.com/frakman1) - Sample code error
* [deajan](https://github.com/deajan) - Custom button text for Popups
* [GRADESK](https://github.com/sidbmw/ICS4U) - Created by a couple of young talented programmers, this classroom management software combines SQL and a GUI to provide a much improved interface for Ottawa teachers.
## 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 cor
<!--stackedit_data:
eyJoaXN0b3J5IjpbNjY5NTQ1Njk0LDcyMDgwODczMiwxNjc5OD
UwOTkyLC0xNDYxNDI4MSwtNjA2MzcxMTgsLTUwOTM1OTEyMywt
MjQ4OTc2MjksMTMwNzY5MjU5LC0yOTY3ODM1NSwtNzc0MDc0Mj
MwLDI2NjM2NDQxNyw0NDk0MzMyNDMsLTExNDg0OTA2MjNdfQ==
eyJoaXN0b3J5IjpbMjYwNTg0ODE0LDExMDIwODgzMzMsMTY3OT
g1MDk5MiwtMTQ2MTQyODEsLTYwNjM3MTE4LC01MDkzNTkxMjMs
LTI0ODk3NjI5LDEzMDc2OTI1OSwtMjk2NzgzNTUsLTc3NDA3ND
IzMCwyNjYzNjQ0MTcsNDQ5NDMzMjQzLC0xMTQ4NDkwNjIzXX0=
-->