Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Announcements #142

Open
MikeTheWatchGuy opened this issue Sep 6, 2018 · 491 comments

Comments

@MikeTheWatchGuy
Copy link
Collaborator

commented Sep 6, 2018

Announcements - New Features, Design Patterns, and Methods

I'm unsure how GitHub sends out updates. I don't think people are informed about Wiki changes for example. I've been announcing new features and more importantly, new ways of doing things, on the Wiki. I'm going to put announcements here so they are more visible. If there are objections about the traffic, well, what can I say, it's a busy/active project.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 6, 2018

New use pattern - Element lookup using Keys

keys can be used to lookup Elements. As a result, all Elements are capable of having a key, including non-output elements such as a Text Element.

To get an element object from a form, you call
form.FindElement(key)

This is the new, preferred method for doing Updates on elements.

Previously if you wanted to output something to a Text Element, you needed to create the text element outside of the form layout and keep that text element variable around so you can call text_element. Update('new text')

The new design pattern is thus:
In your form layout, include a key on your Element:

layout = [[sg.Text('My text', key='text')]]

Later in your code you can update this Text Element by making this call, assuming the variable form is your FlexForm object:

form.FindElement('text').Update('new text')

The Demo programs have all been updated to use this new technique. This capability and its impact on the length of programs led to pushing version 2.30 out the door quickly.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 6, 2018

Borderless Windows are Here

Try them on your next form.
Add this to your FlexForm call:
no_titlebar = True

You can expect to see some of these in the Demo programs.

borderless grayed buttons

You can click anywhere on the window and drag to move it. Don't forget to put an exit key on these windows.

Be sure and make an "exit" button or you'll be running task manager to close your windows. The reason is the when you turn on this option, you will not see an icon on your taskbar for the window. This happens on both Windows and Linux. Thus, if you do not supply an exit button, the user will have no means to close the window.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 7, 2018

Grab Anywhere

Tonight's change is perhaps going to be a really cool thing or one that is going to piss people off.

But, hey, I like it this way. If you don't, setgrab_anywhere = Falsein your call to FlexForm.

As the name implies, you can grab and drag your window using any point on the window, not just the title bar. I was only enabling this when the title bar was turned off. I think it's a much superior way to interact with a window.

FlexForm is becoming quite the call!
def __init__(self, title, default_element_size=DEFAULT_ELEMENT_SIZE, default_button_element_size = (None, None), auto_size_text=None, auto_size_buttons=None, scale=(None, None), location=(None, None), button_color=None, font=None, progress_bar_color=(None, None), background_color=None, is_tabbed_form=False, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON, return_keyboard_events=False, use_default_focus=True, text_justification=None, no_titlebar=False, grab_anywhere=True):

So, enjoy a lazy way of interacting with windows on me.

You will want to turn if off for forms with a SLIDER. you need the slider to move, not the window. I'll update the Demos that use sliders to turn off the grab_anywhere.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 7, 2018

Tables

This one has been requested a number of times. Rather than make a Table Element, decided to see if the current PySimpleGUI is capable of making nice tables using standard Elements. The answer seems to be yes, it's possible with existing Elements. The key was to enable text justification in the InputText element. By right justifying the text in those Elements, it's possible to create a nice looking table.

Here's an example using a ComboBox and Input Elements.

light table

You'll find the code that generated the table in the file Demo_Table_Simulation.py. It requires the latest PySimpleGUI from GitHub in order to use the justification setting.

This is a "live keyboard" demo. It updates the table values as you are typing.

There are 3 fields at the top of the table. If you enter a value into the 3rd field, the cell that the other 2 cells represents will be changed to that value. Enter 1, 2, 1234 and cell (1,2) will be changed to 1234.

There is a new trick demonstrated in this demo that shows off the power of Python. Rather than pass in a string as the key to the Input Elements, I passed a tuple. Nothing about the key requires it to be a string. The only requirement is that you use the same type to look up the element when you call FindElement or use the key to read the return values.

This is the code that makes the Input Elements:

    for i in range(20):
        inputs = [sg.In('{}{}'.format(i,j), size=(8, 1), pad=(1, 1), justification='right', key=(i,j), do_not_clear=True) for j in range(10)]

See how the key is set to (i,j). This allow me to easily find the Element that is represented by (i,j) later. What to access the cell at (0,0)? This you would make this call:
form.FindElement((0,0))

Hopefully this is enough capability for the folks that need tables in their forms/window.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 7, 2018

Three Point Oh!

So maybe I kinda screwed up the numbering when the last one became 2.30. I didn't think about it looking like 2.3 also. Doh!

There have been a lot of changes lately so perhaps it's time for a major bump.

It's a clean slate

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 8, 2018

keep_on_top = True

What might this setting do in a call to FlexForm? If you guessed create a window that's ways on top you're right.

This one little flag enables cool floating toolbars that stay on top of all of your other windows. I'll admit a large portion of this project is for selfish purposes, that I have a platform to develop tools on top of.

Now I've got this nifty toolbar on the top part of my screen, always ready to launch or do something.

floating launcher

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 8, 2018

3.0.2 release today to turn off the grab_anywhere feature for non-blocking forms. tkinter is printing out a warning/error message when the form is closed using a button. Doesn't appear to have any effect on the overall functioning, but it's distressing to see. Better to disable this feature for now.

Plan is to add back an override mechanism should a user want it.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 8, 2018

RELEASED 3.0.2

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 8, 2018

Floating Toolbar - New demo program

This is an always-on-top, compact floating toolbar. They are super-handy to leave running. Something satisfying about writing code that then gets used often, especially if they make you much more efficient.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 9, 2018

Async Forms

Updated the Readme / primary doc to discuss the use of non-block forms.

As explained in the documentation there are a number of techniques to move away from async forms including using the change_submits = True parameter for elements and return_keyboard_events = True

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 9, 2018

Floating Desktop Widgets

I've discovered that in about 30 lines of code you can create a floating desktop widget.

snap0276

If you click the pause button, it switches to Run.

snap0275

This "Widget" is always on top of the other windows.

Looking for a way of launching these in a way that have no taskbar icons. If launched from PyCharm it behaves this way. If launched from a Toolbar, the toolbar's window is attached to the timer. Close it and the timer closes.

This demo is the first time I've ever combined a ReadNonBlocking with a Read in the same form. The reason for using it in this program is that while the timer is paused, there' s nothing happening so why have the program running the loop when it can wait for the user to do something like click a button. When the button is clicked we return from the Read call.

Thank you to jfong for sending an interesting version of this program. His ideas have rolled into a into the project code many times.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 10, 2018

Menus are done

The last of the big features, Menus, was just released to GitHub. With it comes the ability to get the look and feel of a windows program. I don't know if the architecture will lend itself to being used this way or not, but it did seem like a useful feature to add..

snap0204

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 10, 2018

3.7 Support

Thanks to @mrstephenneal we can now say that PySimpleGUI works on Python 3.7. There was a button issue causing trouble. Looks like it's fixed now so I think 3.7 is now safe to with PSG.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 10, 2018

Release 3.01.00

Menus! (and a Listbox.Update bug) are the big features.

Since the Menu code is somewhat isolated, and I want to get some users on it, decided to go ahead and push it all out there in 3.01.00

I didn't mention this in the readme section on menus, but by default (you can't currently turn it off) menus are detachable. If you double-click the dashed line then you get a floating version of that menu. Should make for some pretty interesting user interfaces?

tear off

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 10, 2018

3.1.1

There have been enough bug fixes to trigger another PyPI release. People have been doing more and more with the Update method. These fixes were mostly in those methods.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 11, 2018

Update methods updated

Added the ability to enable / disable all input elements.
Set parameter disable=True to disable, disable=False to enable, disable=None to leave it alone

A number of Demo programs also refreshed.

Expect a PyPI release soon.

Note that some Update method changes also changed parameter names from new_value to value, new_values to values. Some were different than others. Removed new_ so they all match now. Sorry to those living on the bleeding edge!

Here's a before/after. Elements towards the bottom of the window were disabled.

Yes, even buttons can be disabled now. No more needing to gray out your own buttons!

enabled
disabled

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 11, 2018

3.1.2

Big change this time around is the ability to disable widgets. All input widgets have an Update method that has the parameter disabled that you set to True if you want to disable it.

A few critical bugs in there too which pushed up the release to today.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 12, 2018

Resizable Windows, Font settings for input text elements, beginnings of Treeview Element

You can stretch windows bigger now and some of the elements will resize with the window. **

The Input Text Elements did not have a functioning Font setting. Doh! Don't know how that got missed.

The very beginnings of the Treeview element are in there.

Hopefully nothing was broke. Any time I make changes to the core widget packing I get nervous!

** Had to turn off some of the Resizable windows features....Buttons and other elements were moving / expanding in forms that I didn't want the to expand. The change fucked up too many elements to leave on for now.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 12, 2018

Two new Demo programs - CPU Desktop Widget, Spinner Compound Element

Added another Desktop Widget to the demos. This one shows the CPU utilization.

cpu widget

The spinner allows you to change how often it's refreshed

The Spinner Compound Element was done in response from a user wanting to see a different kind of spinner. This one has larger buttons and is laid out horizontally.

spinner compound

The point of this demo is that it's possible to put together multiple Elements into a higher level element. There aren't many of these I can think of at the moment, but given how many user questions are asked, something else is bound to be asked for.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 13, 2018

Table Element, Complete rework of Popups, Death of MsgBox

You can blame the Popup changes on this issue:
#204

All of the Popups were rewritten to use a long list of customization parameters. The base Popup function remained more or less the same.

Decided while I was going all the Popup work that it's time to completely remove MsgBox. Sorry all you early adopters. You'll need to do a bulk rename and then you'll be fine.

Table Elements

Finally have something to show in the form of tables. The element name is Table. While the tkinter Treeview widget was used, many of the parameters were not exposed. If they were, the caller could really mess things up. Better to present a nice "Table-friendly'" interface than something specific to tkinter. After all, the plan is to expand PySimpleGUI to use other GUI frameworks.

A Demo program is in the works.

It's possible to add scrollbars to the Table element by simply placing it into a Column element.

There's still work to do and a good number of bugs, but I encourage you to give it a try.

scrolled table

If you do not put the Table Element inside of a Column, then you can still view and scroll the table, it just will not have scrollbars.

There is a problem currently with keyboard input when placed into a Column. The keyboard keys work fine when NOT inside of the Column but stop working when placed inside a Column Element.

This program will read a CSV file and display it in a window.

import csv
import PySimpleGUI as sg

filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files","*.csv"),))
# --- populate table with file contents --- #
data = []
if filename is not None:
    with open(filename, "r") as infile:
        reader = csv.reader(infile)
        try:
            data = list(reader)  # read everything else into a list of rows
        except:
            sg.PopupError('Error reading file')
            exit(69)

sg.SetOptions(element_padding=(0, 0))

col_layout = [[sg.Table(values=data, headings=[x for x in range(len(data[0]))], max_col_width=8,
                        auto_size_columns=False, justification='right', size=(8, len(data)))]]

layout = [[sg.Column(col_layout, size=(1200,600), scrollable=True)],]

form = sg.FlexForm('Table', grab_anywhere=False)
b, v = form.LayoutAndRead(layout)

It's another bit of PySimpleGUI "challenge code"..... The challenge is to do the same operation in another GUI framework in less lines of code. I would enjoy seeing the tkinter code required to create the window that this 20 line PySimpleGUI program creates. Most of the code deals with reading the CSV file 👍

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 14, 2018

Linux Virtual Environment

I finally installed VirtualBox and am running Ubuntu Linux. I tried to install the Mint distro, but the display was scrambled when it booted.

I was surprised how close the Linux screen shots look to the Windows.

ping graph linux
toolbar linux
all linux
ping linux

Even Pong worked the first time.

I don't believe that Python has been labelled the "go to language" for doing cross-platform GUI work. I guess I never stopped to think about it. I don't recall seeing this kind of thinking in posts or books I've read on Python. Perhaps it's time for that to change?

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 14, 2018

3.2.0

Released a new release to PyPI. Sorry about all these releases, but features continue to pour into the code. I'm finding even the folks that are actively using PySimpleGUI only run the pip installed version rather than the GitHub version. That means if I want runtime on the code, I'm only going to get any is to do a full release.

There were a number of changes that could f-up, so be on the lookout. The biggest addition to 3.2.0 was the Table Element (beta quality at the moment).

If you are running older programs then you may crash due to missing functions, MsgBox and several others. This is because I've moved 100% to Popup calls. It's not like I haven't been warning people so I don't expect complaints.

Some people are calling ReadNonBlocking prior to your Event Loop so that the form gets fully made. This call is needed if you want to perform actions on elements prior to calling Read. For example, if you want your form to be shown with some Elements set in the disabled state (using calls to Update), you will need to make an additional call after your Layout call.

Instead of calling ReadNonBlocking in these situations, you can call Finalize/PreRead/PrepareForUpdate. I have not been able to standardize on a name, so I'm providing multiple. I'm sure a winner will emerge. I've been using Finalize.

The call sequence becomes this:

form.Layout(layout)
form.Finalize()
element.Update(.....)
while True:
     b, v = form.Read()

You'll also find the Finalize call used in the scripts that use the Canvas Element.

See the Readme for more info on what's in the release. Note that the readme has not yet been updated with the Table Element and several other changes. There's only so much I can do.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 15, 2018

One Line Progress Meters

PySimpleGUI has always had a one-line progress meter called EasyProgressMeter. However, that function has a limitation of only 1 meter being active at a time.

The new way to do Progress Meters is the function OneLineProgesssMeter.

All of the documentation and examples will reflect this new function.

Have to say it's nice to be able to run as many meters as desired without having to worry about more than 1 being on the screen at a time.

I intend to remove EasyProgressMeter within the next 5 or 6 releases to PyPI. I tried to insert a warning in the code, but too much code was shared to fit the message in.

I'm sorry about the change, but really would like to both add this function and rename the capability to something very descriptive. If there is enough revolt over removing EasyProgressMeter, I'll leave it in and simply drop it from all the documentation.

onelineprogressmeters

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 15, 2018

3.3.0

Yea, yea, it seems like only yesterday that version 3.2.0 was released. That's because it WAS only yesterday. I've been busy.

There are 2 changes I wanted out quickly....

  1. The ability to turn off displaying row numbers
  2. The new OneLineProgressMeter function

The Progress Meter feature alone is a great use of PySimpleGUI. A number of users are using it only for this purpose in their programs.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 16, 2018

Graphing

New demo program - graph ping using canvas.
I'm thinking about creating a Graph Element, something that makes it super easy to users tog create graphs, both line and x,y plot. The demo should how to take a canvas element and graph ping times.

There is another ping-graph demo using Matplotlib. This graph only uses tkinter.

Finally, because the pings take a long time, I moved the ping calls outside of the GUI event loop. Calling ping inside event loop was causing the GUI to respond sluggishly. This is because the ping was taking 1 second which means the gui wasn't being refreshed / wasn't responsive during the second. Now the GUI sleeps for 200 ms while the ping is done by a thread.

This is yet another toe in the water with threading. The problems I saw in the past are no longer there, it would appear.

I also checked in the ping.py file that you need for this demo. It's a pure python implementation of ping and works pretty well, even if slow.

ping graph

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 16, 2018

Progress Meters

Thanks to @JorjMcKie I've learned more about the performance of the EasyProgressMeter and thus probably the OneLineProgressMeter. The more arguments to display the longer it takes.

Was going to document in the Cookbook / Readme that if you have performance concerns, you can call the progress meter less frequently. You don't have to update it 1 count at a time. It could be like this:

for i in range(10000):
    if i % 5 == 0: sg.OneLineProgressMeter('My 1-line progress meter', i+1, 10000, 'single')

This meter is only called every 5 times through the loop. It finished quite a bit quicker than the test updating the meter every single time.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 16, 2018

PySimpleGUI programs as an EXE file!

The biggest thing to hit PySimpleGUI since Colors.... the ability to run programs written for PySimpleGUI as an exe file. ALL credit goes to @JorjMcKie for this.

There is no need to distribute Python with your programs. It's all included in the exe and folder of supporting files.

From what I understand of nuitka, this code is compiled C++ code, not python code. The performance is thus potentially better! It's the best of both worlds.

Working to get the process documented. It's tricky and required a special script. Stay tuned....

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 16, 2018

Graph Element

This one is pretty exciting as it does something new on the screen. The Graph Element allows you to easily create a canvas and draw on it using your own coordinate system. You don't need to do conversions from your graph coordinates to the tkinter canvas graph coordinates.

The Demo program for it is a good example. It displays a pint graph. The graph we're creating is a line graph what we would like to to from 0,0 in the bottom left to 100, 500 in the upper right. This will give us 100 data points along the x axis and up to 500 ms on the y axis.

After creating the Graph Element, we can do 3 operations on it:

  1. Draw Line
  2. Draw Point
    3 Erase

The draw line draws a line from 1 point to another. The points are specified using your graph coordinates, not the tkinter canvas coordinates.

snap0282

I know I have a LOT of documentation to do.

In the meantime, try using Control+P if you're using PyCharm. Press Control+P while you are typing in the parameters and you'll see a popup showing you what the legal parameters are. This feature is almost necessary when using PySimpleGUI because functions have SO many optional parameters.

snap0283

I hope to see some cool creations using the capability. I'm starting to see more and more projects pop up on GitHub that use PySimpleGUI! Keep those examples coming! And keep the requests for new features coming too. They have made this such a better package because of your help.

Sample code:

This is your layout:

    layout = [  [sg.T('Ping times to Google.com', font='Any 18')],
               [sg.Graph((300,300), (0,0), (100,500),background_color='white', key='graph')],
               [sg.Quit()]]

    form = sg.FlexForm('Canvas test', grab_anywhere=True)
    form.Layout(layout)

To draw a line, call DrawLine:

form.FindElement('graph').DrawLine(from_point, to_point)

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 17, 2018

Movable Graph Element

Made the Graph Element "movable". This means the graph can be shifted when it reaches the "End".

Here's a 1,000 data-point ping graph or 16 minutes woth of pining

scrollingping

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 12, 2019

More on docs....

Of course the best place to READ the docs is on ReadTheDocs (http://www.PySimpleGUI.org). There you'll get a nice table of contents down the left side. It's easier to navigate than the plain readme.

Last post showed an image of what happens when you put your cursor on a PySimpleGUI Element in your code. Let's see what happens when you do this with the other GUI packages.

Qt.... how about QRadioButton as an example:

image

hmmmm.... not much there... surely a button will have info...

Got some parameters this time, but zero explanation
image

If you're using tkinter instead of Qt it's even worse. Every single tkinter call shows the same general purpose document:
image

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 17, 2019

image

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 19, 2019

Python 3.7.4 tkinter Is BROKEN - Not supported by PySimpleGUI

After much time spent by myself and multiple other parties, I've shown that the tkinter that's installed with Python 3.7.4 is broken. It does not display Table colors.

Python 3.7.2 does work and will be the highest level of Python 3.7 that PySimpleGUI will support for the time being. I'm not going to spend another minute debugging tkinter for the 3.7 team.

I'll add this to the readme / read the docs.

Sorry to all those that wasted time on this.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 20, 2019

New features for tables

I completed the Table colors Update support for Tables in PySimpleGUI. You can set the text and background colors of tables using the Update method.

This particular feature and the massive addition of docstrings is inching us towards a 4.1 PySimpleGUI release.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 20, 2019

Ability to set background color of Tooltips using PySimpleGUI

I have exposed the background color for tooltips. You can set the variable:
TOOLTIP_BACKGROUND_COLOR
to any color string you want. You need to change the variable prior to creating your layout. Do it before making any PySimpleGUI calls.

import PySimpleGUI as sg

sg.TOOLTIP_BACKGROUND_COLOR = "#ffffe0"
@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 20, 2019

The cruel GitHub Issue Searching Results

As you can see I use GitHJub Issues for a lots of things besides just issues. The reason is really simple, when searching GitHub Issues for a problem, I definitely want these "notes", announcements, suggestions, templates, screenshots,etc, to be searched as well.

The cruel part is that GitHub shows results for these really long Issues, but when you open it up, you find THIS in the middle of the Issue:
image

WHAT!? Over 400 entries written are hidden. OK, so I manage to find this little box and I click it. What happens? Well, I see more, but then it's replaced with another little box

image

Oh, great, I'm "down to only'" 356 hidden entries". Search for "hidden entries", click again, searching, click, lather rinse repeat.

This happens for these Announcements as well as the screenshots.

I've tried to find ways of turning this off, but nothing has worked. So, it's time to break them apart so that all entries can be seen. It may take splitting into 3 or 4 or more Issues, but it's going to be worth it for sure as a lot of screenshots are hidden and a lot of good knowledge hidden as well.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 23, 2019

Pull Requests

Just updated the Issue that has the "Pull Request Policy" in it. I thought that since it's so short I'll just copy it here too.

TLDR - Not taking pull requests for core code. I'll still listen to your suggestions.

Pull Request Policy

I've talked about Pull Requests several times over the past year in the Announcements Issue. But, the way GitHub works, it hides a LOT of posts when you look at an Issue. The result is that even if you search for Pull in the Announcements Issue, you will not find the full set of posts.

Pull Requests - Not taking pull requests for core code

If you have a bug fix or suggestion for enhancement in the core code, I'm still interested in taking a peek, but I'm not doing direct merges.

Pull requests are already rare so I don't think it's going to affect much of anything.

There are a lot of reasons that I won't bother listing. Let's just sum it up as more efficient, much happier without them.

This first year has gone really well. I fully expect year 2 to be just as productive.

Still Have Something To Share?

Some users are submitting their "suggested code changes" as part of the Issue they are using to report a bug. I love these kinds of fixes and suggestions. I find them easier to digest and they don't feel like a bag of code has just been dropped into my lap that I must deal with.

This will rub a few the wrong way and for that I'm really sorry. Perhaps it's not in the spirit of Open Software to work in this manner but I hear from a lot more happy users right now than unhappy ones and so this really shouldn't have an impact.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 23, 2019

Updated Readme/ReadTheDocs and Cookbook

I'm REALLY sorry to all the newcomers that hit example #1 in the readme, the example using the "Designer tool" (pencil and paper). The problem is that the values coming back from a Window.Read call are going to be a dictionary rather than a list. These early example bits of code attempted to unpack the dictionary like a list which leads to crashes.

Instead of the pleasant 5 minute install and run, you likely had a 2 minute install process followed by a really large amount of time spent on trying to get the example to run. Sh*t! I'm really sorry about that.

Both the Cookbook and the main documentation have undergone a number of changes to both explain things better and removal of this kind of non-working crap! It all just got a little out of control there for a bit.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 4, 2019

Slow Going - FINALLY cramming out a release for PySimpleGUI 4.1

Sorry that it's so slow the past few weeks. These docs have been a real chore and continue to. There has never been such a long time between releases.

I'm pulling together release notes now, but it's going to take a while as there are 616 changes since to the last PySimpleGUI release and each are checked before the release will go out.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 4, 2019

PySimpleGUI 4.1 Anniversary Release! 4-Aug-2019

Long time coming. Docstrings continue to be a focus.

NOTE the Python 2 version is still in the works.

  • Version can be found using PySimpleGUI.version
  • New bit of licensing info at the top of the file
  • Types used in the doc strings. Also type hints in some comments. Because also running on 2.7 can't use full typing
  • Added using of Warnings. Just getting started using this mechanism. May be great, maybe not. We'll see with this change
  • Added TOOLTIP_BACKGROUND_COLOR which can be changed (it's tkinter only setting however so undertand this!)
  • Graph.DrawText. Ability to set text_location when drawing text onto a Graph Element. Determines what part of the text will be located at the point you provide when you draw the text. Choices are:
    • TEXT_LOCATION_TOP
    • TEXT_LOCATION_BOTTOM
    • TEXT_LOCATION_LEFT
    • TEXT_LOCATION_RIGHT
    • TEXT_LOCATION_TOP_LEFT
    • TEXT_LOCATION_TOP_RIGHT
    • TEXT_LOCATION_BOTTOM_LEFT
    • TEXT_LOCATION_BOTTOM_RIGT
    • TEXT_LOCATION_CENTER
  • Flag ENABLE_TK_WINDOWS = False. If True, all windows will be made using only tk.Tk()
  • SetFocus available for all elements now due to it being added to base class. May NOT work on all elements however
  • Added Combo.GetSElectedItemsIndexes() - returns a list of all currently selected items
  • Fixed Listbox.Update - set_to_index changed to be an int, list or tuple
  • Added parent parameter to call to tkinter's askopenfilename, directory, filenames. Not sure why the root wasn't passed in before
  • Button.Update - also sets the activebackground to the button's background color
  • Graph - New parameter when creating. float_values. If True, then you're indicating that your coordinate system is float not int based
  • Graph.Update - made background color optional parm so that visible only can be set
  • Frame.Layout returns self now for chaining
  • TabGroup.Layout returns self now for chaining
  • Column.Layout returns self now for chaining
  • Menu.Update menu_definition is now optional to allow for changing visibility only
  • Added inivisiblity support for menu bars
  • Table.Update supports setting alternating row color and row_colors (list of rows and the color to set)
  • Set window.TimeoutKey to TIMEOUT_KEY initially
  • Window - check for types for title (should be string) and layout (should be list) and warns user if not correct
  • Window - renamed some methods by adding _ in front (like Show) as they are NOT user callable
  • Another shortcut! Elem = Element = FindElement
  • SaveToDisk - will not write buttons to file. Fixed problems due to buttons having keys
  • Remapped Windowl.CloseNonBlockingForm, Window.CloseNonBlocking to be Window.CloseNonBlocking
  • Fix for returning values from a combo list. Wasn't handling current value not in list of provided values
  • Spin - Returns an actual value from list provided when Spin was created or updated
  • Chaneged FillFormWithValues to use the new internal AllKeysDict dictionary
  • Added try when creating combo. Problem happens when window is created twice. Prior window had already created the style
  • Added list of table (tree) ids to the Table element
  • Enabled autoclose to use fractions of a second
  • Added a try around one of the destroys because it could fail if user aborted
  • Popup - Icon is no longer set to default by default
  • Fix for debugger trying to execute a REPL comand. The exec is only avilable in Python 3
  • main() will display the version number in big letters when program is running
@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 8, 2019

It's been a minute

The documentation beat goes on. It's what most all of the time is spent on, in addition to support.

There are features and fixes going into all ports. Today I think all ports except Wx got some code change to them.

There has been so much going on and yet so little at the same time. I think spreading outward in all directions is about the best way to describe it.

Column sizes work now in PySimpleGUI (on GitHub)
This was a troubling problem that I finally managed to get my arms around. Setting a specific size now works for BOTH scrollable and non-scrollable columns. I predict this will have a very positive impact to layouts.

New documentation sections OTW include:

  • Layout control (moving elements around)
  • Generating versus declaring layouts (writing 3 or 4 lines of code that produces a window with 30 lines of elements)
  • User Defined Elements
  • User Expansion of PySimpleGUI through direct framework access
  • Patching PySimpleGUI
  • The built-in debugger
  • Doc strings for the other 3 ports

Thanks for all the inspiration folks!

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 12, 2019

Micro-Demos - Small programs that do something useful and can be pasted

Sorry for a lengthy post, but it does have 4 complete programs and 2 screen shots.

What makes these Micro-Demos fun is that they are short enough to "post" on a forum or GitHub Issue or on Reddit. No link to follow, just a short complete program. No code fragment to integrate either.

There have been a number of awesome features added / completed in PySimpleGUI (the main version). The reason PySimpleGUI tends to be more complete, mature is that it's the flagship of the family of ports. It's the proving ground as well for new SDK changes.

Demo 1A - Viewing a webcam in a GUI window using OpenCV - compacted

Modified existing demo to create a super-simplified, get the point through, demo. It's a whopping 13 lines of code if compacted:

import PySimpleGUI as sg
import cv2
layout = [[sg.Image(filename='', key='image')],]
window = sg.Window('Demo Application - OpenCV Integration', layout, location=(800,400))
cap = cv2.VideoCapture(0)
while True:
    event, values = window.Read(timeout=20, timeout_key='timeout')
    if event in ('Exit', None):
        break
    ret, frame = cap.read()
    imgbytes=cv2.imencode('.png', frame)[1].tobytes()
    window.FindElement('image').Update(data=imgbytes)
window.Close()

Demo 1AA - Ultra Compact

This is just plain showing off. It's 7 lines in total. 8 if you add the CR to my if statement ;-)

import cv2, PySimpleGUI as sg
window = sg.Window('Demo Application - OpenCV Integration', [[sg.Image(filename='', key='image')],], location=(800,400))
cap = cv2.VideoCapture(0)
while True:
    event, values = window.Read(timeout=20, timeout_key='timeout')
    if event is None:  break
    window.FindElement('image').Update(data=cv2.imencode('.png', cap.read()[1])[1].tobytes())

But that's not very readable, so let's go back to the original for this one. It's "fancy". It doesn't have a titlebar for example. And, if you uncomment the line for the right click menu, you'll be able to exit the application if you're not running in an IDE.

Demo 1B - Viewing a webcam in a GUI window using OpenCV - commented

import PySimpleGUI as sg
import cv2

def main():
    sg.ChangeLookAndFeel('Black')

    # define the window layout
    layout = [[sg.Image(filename='', key='image')],]

    # create the window and show it without the plot
    window = sg.Window('Demo Application - OpenCV Integration', layout,
                       location=(800,400), no_titlebar=True, grab_anywhere=True,)
                       # right_click_menu=['&Right', ['E&xit']],)

    # ---===--- Event LOOP Read and display frames, operate the GUI --- #
    cap = cv2.VideoCapture(0)
    while True:
        event, values = window.Read(timeout=20, timeout_key='timeout')
        if event in ('Exit', None):
            break
        ret, frame = cap.read()
        imgbytes=cv2.imencode('.png', frame)[1].tobytes()
        window.FindElement('image').Update(data=imgbytes)

    window.Close()

main()

What is downright extraordinary is that no special code was put in PySimpleGUI to support OpenCV. This just naturally happens if you write the right code. The code only uses PySimpleGUI and OpenCV.

Finally, it works on PySimpleGUIQt and PySimpleGUIWeb, you guessed it... by changing the import statement. STUNNED is how I feel every time this works. BTW, the Web version flickers and is being worked by the Remi team now.

Here's the window it produces
image

Demo 2 - Drawing On Top of An OpenCV Stream - Released as Demo_OpenCV_Draw_On_Webcam.py

This was a feature asked about and spurred 2 new Graph methods - BringFigureToFront, SendFigureToBack . The only way to "Draw" on something in PySimpleGUI is to use a Graph Element. The poster asked about using a mouse to draw.

The Graph Element mouse Dragging was finally completed this past week. The "Mouse Up" events were missing.

In order to "Draw" on a Graph, you need control of the Z-Order of the figures that are being drawn. In this case, the webcam image needs to be behind whatever is being drawn, thus the 2 new methods.

Without further delay, a webcam stream that you can draw red circles all over while it streams.

import PySimpleGUI as sg
import cv2

def main():
    layout = [[sg.Graph((600,450),(0,450), (600,0), key='_GRAPH_', enable_events=True, drag_submits=True)],]

    window = sg.Window('Demo Application - OpenCV Integration', layout)

    graph_elem = window.Element('_GRAPH_')      # type: sg.Graph

    id = None
    # ---===--- Event LOOP Read and display frames, operate the GUI --- #
    cap = cv2.VideoCapture(0)
    while True:
        event, values = window.Read(timeout=0)
        if event in ('Exit', None):
            break

        ret, frame = cap.read()
        imgbytes=cv2.imencode('.png', frame)[1].tobytes()
        if id:
            graph_elem.DeleteFigure(id)             # delete previous image
        id = graph_elem.DrawImage(data=imgbytes, location=(0,0))    # draw new image
        graph_elem.SendFigureToBack(id)
        if event == '_GRAPH_':
            graph_elem.DrawCircle(values['_GRAPH_'], 5, fill_color='red', line_color='red')
    window.Close()

main()

Demo 3 - "Dragging" a Selection Rectangle

We've all used a paint program where you click, drag your mouse and it creates a rectangle that matches the size specified by releasing the mouse button. Click, hold, drag, release. AS you are dragging, a rectangle is drawn, erased if you move the mouse, then redrawn. The result looks like an animation

Evidently this is called a "Rubber Band Selection Rectangle". I learn something new every day from PySimpleGUI. This code was adapted from some user code. In particular I lifted the rectangle logic. However, the original was using PIL to draw a rectangle on the image itself prior to the image being displayed. While this worked, it consumed a lot of compute power and thus wasn't as responsive as it could be.

The more efficient way to create one of these rectangles is to use the Graph element's DrawRectangle method. IT's super fast and will work on other ports of PySimpleGUI once the mouse code is completed in those ports (PySimpleGUIQt comes to mind)

import PySimpleGUI as sg

# image_file = r'C:\Python\PycharmProjects\GooeyGUI\logo_black.png'
image_file = None       # image is optional

layout = [  [sg.Graph(canvas_size=(400, 400), graph_bottom_left=(0, 400), graph_top_right=(400, 0), key="graph", change_submits=True, drag_submits=True),],
            [sg.Text("", key="info", size=(60, 1))]]

window = sg.Window("Drag a Rectangle", layout).Finalize()     # need Finalize due to DrawImage call prior to Read

graph = window.Element("graph")     # type: sg.Graph

graph.DrawImage(image_file, location=(0,0)) if image_file else None
dragging = False
start_point = end_point = prior_rect = None
while True:
    event, values = window.Read()
    if event is None:
        break  # exit
    if event == "graph":
        graph.DeleteFigure(prior_rect)      # don't worry, PSG won't complain if bad ID
        if not dragging:
            start_point = values["graph"]
            dragging = True
        else:
            end_point = values["graph"]
            prior_rect = graph.DrawRectangle(start_point, end_point, line_color='red')
    elif event.endswith('+UP'):
        window.Element("info").Update(value=f"grabbed rectangle from {start_point} to {end_point}")
        # enable grabbing a new rect
        start_point = end_point = None
        dragging = False
    else:
        print("unhandled event", event, values)

RubberBand

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 12, 2019

'Combo', 'Tab', 'TabGroup' Changes - Import API calls deprecated

I'm really sorry if you implemented code using some recently released API changes.

It's rare to flat out break the PySimpleGUI API, but this is a "band-aid" moment. The changes were recent, the number of users that likely used the deleted methods are few. So, rather than slowly remove them over time, they just disappeared and were replaced by others that will give you the data you need.

Rules are meant to be broken, but not architectures. In this case, the "don't break the install base's code" rule is broken, but as explained, the size of the impact is relatively small, but I DO feel the pain of those that are impacted. I'm really sorry about this.

The problem was that I hastily made some changes to the code that don't fit the big-picture architecture of PySimpleGUI. Without boring you too much - basically I added some stuff that uses "indexes" and I named some methods in inconsistent ways or I didn't implement the right method.

ComboBox and TabGroup changes

There was a recent addition that returned the "Currently selected item" in terms of "index". This isn't how it's been done across all the ports for the other elements. The remedy is to supply methods that fit the architecture.

Combo.Get() & TabGroup.Get()

Some elements provide a Get method. This returns the "Current" (as in right now) value for an element. It's identical in value to what would have been returned from a call to "window.Read()`. A number of elements have this.

This method does sometimes confuse people. Newcomers, when seeing this method, sometimes think it has to be called in order to get the element's value. They don't realize that ALL element values are returned in the values portion of the window.Read call.

This Get method was added to the TabGroup Element so that the currently active tab is obtained. It was added to the ComboBox so that the current item can be read. Again, these are identical to what you will see in your call to window.Read so use that data if you can instead of calling Get.

Tab.Select()

A requested feature recently was the ability to make a particular tab the "active" or "selected" tab in its group. This is similar to the recently added Button.Click. In the last release, it was implemented as a call to the TabGroup where an index was passed to the group to select a tab. This didn't follow the norm of acting directly on an element that you want to take the action. In this case, it's the tab itself that's taking an action. You could say that it's the TabGroup. From an Object kind of look, operating on the Tab made more sense.


Again, I'm sorry about this change. I have been too buried in the documentation overhaul, cookbook and demo programs. It's been a while since this kind of new capability was added and I simply didn't take the time to look at the PySimpleGUI APIs as a whole prior to implementing.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 15, 2019

Another dumb but fun demo - realtime ASCII movie from your webcam

from PIL import Image
import numpy as np
import PySimpleGUI as sg; font_size=6
# import PySimpleGUIQt as sg; font_size=8            # if using, be sure and use the second layout that is commented out
# import PySimpleGUIWeb as sg; font_size=12           # yes, it runs in a webpage too!
import cv2

"""
    Interesting program that shows your webcam's image as ASCII text.  Runs in realtime, producing a stream of
    images so that it is actually animated ASCII text. Wild stuff that came about from a post on Reddit of all
    places.  The software bits that turn the image into ASCII text were shamelessly taken from this gist:
    https://gist.github.com/cdiener/10491632
    Brilliant work to have pulled off so much with so little Numpy
    What's remarkable about this program is that the animation is created by updating individual Text Elements going
    down the window, one line at a time, every time through the loop.  That's 48 lines of text every time. Rough
    timing shows an animation of more than 10 fps when running any of the PySimpleGUI ports.
"""

# The magic bits that make the ASCII stuff work shamelessly taken from https://gist.github.com/cdiener/10491632
chars = np.asarray(list(' .,:;irsXA253hMHGS#9B&@'))
SC, GCF, WCF = .1, 1, 7/4

sg.ChangeLookAndFeel('Black')   # make it look cool

# define the window layout
NUM_LINES = 48  # number of lines of text elements. Depends on cameras image size and the variable SC (scaller)
layout =  [*[[sg.T(i,size=(120,1), font=('Courier', font_size), key='_OUT_'+str(i))] for i in range(NUM_LINES)],
          [ sg.Button('Exit')]]

# if using PySimpleGUIQt, use this layout instead.  The text rows are too far apart otherwise.
# layout =  [*[[sg.T(i, size_px=(800,12), font=('Courier', font_size), key='_OUT_'+str(i))] for i in range(NUM_LINES)],
#           [ sg.Button('Exit')]]



# create the window and show it without the plot
window = sg.Window('Demo Application - OpenCV Integration', layout, location=(800,400),
                   no_titlebar=True, grab_anywhere=True, element_padding=(0,0))

# ---===--- Event LOOP Read and display frames, operate the GUI --- #
cap = cv2.VideoCapture(0)                               # Setup the OpenCV capture device (webcam)
while True:
    event, values = window.Read(timeout=0)
    if event in ('Exit', None):
        break
    ret, frame = cap.read()                             # Read image from capture device (camera)

    img = Image.fromarray(frame)  # create PIL image from frame
    # More magic that coverts the image to ascii
    S = (round(img.size[0] * SC * WCF), round(img.size[1] * SC))
    img = np.sum(np.asarray(img.resize(S)), axis=2)
    img -= img.min()
    img = (1.0 - img / img.max()) ** GCF * (chars.size - 1)

    # "Draw" the image in the window, one line of text at a time!
    for i, r in enumerate(chars[img.astype(int)]):
        window.Element('_OUT_'+str(i)).Update("".join(r))
window.Close()

It's posted in the demo section as Demo_OpenCV_Webcam_ASCII.py, but am posting here for easy copy and paste.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 15, 2019

THANK YOU FOR FILLING OUT THE ISSUE FORMS

Damn, we've got the best users ever here!

It's a pain in the ass to fill out an Issue form, answer the billion questions including perhaps an uncomfortable experience level one.

The experience level really helps make sure stuff is explained at a level you're likely able to easily digest. You've not going to be satisfied if a response goes way over your head and you'll be even more unsatisfied if the responses are well under your years of experience.

The experience question is not meant to embarrass or demean or any other nefarious purpose. It's an honest attempt to provide better support. Besides, you have nothing to be embarrassed about!! None of us was born with programming skills. It's a 100% learned skill and every person here had a very first day of using Python.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 16, 2019

More ASCII movies

Did one more pass at this thing. Comments removed to save space.

Controls were added to the bottom. The latest GitHub made them larger for easier use, but's not shown in the movie.

Here's a movie showing what it's like to mess around with controls:
https://youtu.be/rzvJuz5bHTg

The ability to modify one or many parameters and see the result, in realtime, is where PySimpleGUI excels greatly over a command line only option. Just by clicking a spinbox or sliding a slider I can instantly see the effect it has.

Anywhere that "Tuning" is part of the process, a GUI can potentially greatly help. The model - [Running code, stopping, changing variable in code, running code again] cannot compete with click a button and instantly see the change. CLI vs GUI people, continue to debate this as long as you want.

This same kind of "parameter modification" was used in the YOLO demo. You can easily see how changing the "Threshold" has an impact on the classifier.

So, if you want to interact more with your program directly, consider exposing the knobs to turn in a GUI. With PySimpleGUIQt you could literally make them knobs 😏

Here's the modified program that also removes the need to modify anything except the 3 lines at the top in order to move from one platform to another.

Finally, notice this program "Generates the Layout". This topic is being added to the docs.... how to "Build" a GUI layout using Python code. In this case, 48 lines of Text Elements were created in a single statement.

from PIL import Image
import numpy as np
import PySimpleGUI as sg; font_size=6; USING_QT=False
# import PySimpleGUIQt as sg; font_size=8; USING_QT=True            # if using, be sure and use the second layout that is commented out
# import PySimpleGUIWeb as sg; font_size=12; USING_QT=False           # yes, it runs in a webpage too! Not as good as tkinter but works
import cv2

# The magic bits that make the ASCII stuff work shamelessly taken from https://gist.github.com/cdiener/10491632
chars = np.asarray(list(' .,:;irsXA253hMHGS#9B&@'))
SC, GCF, WCF = .1, 1, 7/4

sg.ChangeLookAndFeel('Black')   # make it look cool

# define the window layout
NUM_LINES = 48  # number of lines of text elements. Depends on cameras image size and the variable SC (scaller)
if USING_QT:
    layout = [[sg.T(i, size_px=(800, 12), font=('Courier', font_size), key='_OUT_' + str(i))] for i in range(NUM_LINES)]
else:
    layout =  [[sg.T(i,size=(120,1), font=('Courier', font_size), pad=(0,0), key='_OUT_'+str(i))] for i in range(NUM_LINES)]

layout += [[ sg.Button('Exit', size=(5,1)),
            sg.T('GCF', size=(4,1)), sg.Spin([round(i,2) for i in np.arange(0.1,20.0,0.1)], initial_value=1,  key='_SPIN_GCF_', size=(5,1)),
            sg.T('WCF', size=(4,1)), sg.Slider((1,4), resolution=.05, default_value=1.75, orientation='h', key='_SLIDER_WCF_', size=(15,15))]]

# create the window and show it without the plot
window = sg.Window('Demo Application - OpenCV Integration', layout, location=(800,400), font='Any 18')

# ---===--- Event LOOP Read and display frames, operate the GUI --- #
cap = cv2.VideoCapture(0)                               # Setup the OpenCV capture device (webcam)
while True:
    event, values = window.Read(timeout=0)
    if event in ('Exit', None):
        break
    ret, frame = cap.read()                             # Read image from capture device (camera)

    img = Image.fromarray(frame)  # create PIL image from frame
    GCF = float(values['_SPIN_GCF_'])
    WCF = values['_SLIDER_WCF_']
    # More magic that coverts the image to ascii
    S = (round(img.size[0] * SC * WCF), round(img.size[1] * SC))
    img = np.sum(np.asarray(img.resize(S)), axis=2)
    img -= img.min()
    img = (1.0 - img / img.max()) ** GCF * (chars.size - 1)

    # "Draw" the image in the window, one line of text at a time!
    for i, r in enumerate(chars[img.astype(int)]):
        window.Element('_OUT_'+str(i)).Update("".join(r))
window.Close()
@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 17, 2019

window['key'] can replace window.FindElement('key')

In case you missed the excited announcement today of this new capability, be sure and check it out:

#1827

Briefly..... you can completely remove your calls to window.Element or window.FindElement and replace them with calls to:
window['key']

The code continues to compact!! This one in particular has been bugging me for a long time. I don't like "chaining" for new comers sake. And especially would like to make changing an element's settings and values easier.

Now the line of code to change an element allows your eyes to focus on the SINGLE call presented in the line of code, the call to Update

THE OLD WAY of updating an element:

window.FindElement(key).Update(value)

THE NEW WAY of updating an element:

window[key].Update(value)

This has been one of the most exciting changes since making return values a dictionary.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 17, 2019

Layout Generation

There are 3 ways I "grow" PySimpleGUI and my own knowledge.

  1. Read other people's PySimpleGUI code. The author can have known Python for 2 weeks and still will be able to teach me something. Even simply layout can have some touch to them that I've not thought of. I like to see them ALL!! So please don't be shy.

  2. The Reddit Challenges - There is no official contest or code challenge other than people describing their problem and then ask for a GUI recommendation. I bunch of stuff have come out of these. Most recently was an idea of showing your webcam's video stream as an ASCII image movie, a video of you through your webcam and show it in realtime in a window. So I wrote this program.

What is downright AMAZING about this program is that the way it is being shown to you is that each Text Element is Updated every time I read a webcam image and want to show it as ASCII. 48 Text Elements. I was SURE all the ports would fail, but they all worked GREAT.

If you missed the movie, here it is https://youtu.be/rzvJuz5bHTg
The screen capture didn't do it justice as it's quite smooth (runs in Remi BTW too)

  1. I want a tool or something else to make my life even lazier / efficient

(4) I cheat and have some brilliant friends that help me out, or give me solutions. They deserve just as much credit as anything else listed here.

Generating Layouts

Believe it or not all that leads up to the topic of "Generating Layouts" which means Python code that creates a layout rather than it being created only by hand.

In the process of solving the problems above, especially the Reddit ones, I write more layouts, and have more opportunities to ponder more compact or more "Pythonic" (not found of tossing that word around) ways of doing things.

How to generate or create layouts differently than the brute force ways but rather by machine and by hand have not yet been added to the documentation. It's the hot topic for me today to finish.

Generating Layouts Demo ([Demo_Layout_Generation.py]

(https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Layout_Generation.py))

There are 8 different Design Patterns presented, although it'll be difficult for you to just copy and use them other than to study. Still, it's nice to have when you need to create a sh*t-load of buttons or input fields.

While used and not at all pointed out, I'll stress it in the docs, but keys can be ANYTHING. They're not limited to strings. This means they can hold something like (x,y) data. Each element in a matrix can have a key that is (row, col). The Questionnaire examples illustrate exactly what I'm talking about.

More Python Stuff, More Simple Coming....

Hoping to finish (miracles do happen) the readme and push out release(s) by the end of tomorrow.

There's a concept called "User Elements" that I don't see many people using that will be part of the docs too.

Plus even more Python stuff being looked at now. It's amazing what this language can do for this problem.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 17, 2019

Generating Layouts Code

What the heck, the code for all these layouts isn't very large, so here it is. No download needed. Maybe it'll spark an idea in your head that I can take too.

import PySimpleGUI as sg
"""
    Construct #0 - List comprehension to generate a row of Buttons

    Comprehensions are super-powers of Python.  In this example we're using a comprehension to create 4 buttons that
    are all on the same row.
"""


def layout0():
    layout = [[sg.Button(i) for i in range(4)]]     # A list of buttons is created

    window = sg.Window('Generated Layouts', layout)

    event, values = window.Read()

    print(event, values)
    window.Close()

"""
    Construct #1 - List comprehension to generate rows of Buttons

    More list super-power, this time used to build a series of buttons doing DOWN the window instead of across

"""

def layout1():
    layout = [[sg.Button(i)] for i in range(4)]    # a List of lists of buttons.  Notice the ] after Button

    window = sg.Window('Generated Layouts', layout)

    event, values = window.Read()

    print(event, values)
    window.Close()


"""
    Construct #2 - List comprehension to generate a row of Buttons and concatenation of more lines of elements

    Comprehensions are super-powers of Python.  In this example we're using a comprehension to create 4 buttons that
    are all on the same row, just like the previous example.
    However, here, we want to not just have a row of buttons, we want have an OK button at the bottom.
    To do this, you "add" the rest of the GUI layout onto the end of the generated part.
    
    Note - you can't end the layout line after the +. If you wanted to put the OK button on the next line, you need
    to add a \ at the end of the first line.
    See next Construct on how to not use a \ that also results in a VISUALLY similar to a norma layout
"""

def layout2():
    layout = [[sg.Button(i) for i in range(4)]] + [[sg.OK()]]  # if want to split, can't add newline after + to do it

    window = sg.Window('Generated Layouts', layout)

    event, values = window.Read()

    print(event, values)
    window.Close()


"""
    Construct # 3 - Adding together what appears to be 2 layouts
    
    Same as layout2, except that the OK button is put on another line without using a \ so that the layout appears to
    look like a normal, multiline layout without a \ at the end
    
    Also shown is the OLD tried and true way, using layout.append.  You will see the append technique in many of the
    Demo programs and probably elsewhere.  Hoping to remove these and instead use this more explicit method of +=.
    
    Using the + operator, as you've already seen, can be used in the middle of the layout.  A call to append you cannot
    use this way because it modifies the layout list directly.
"""

def layout3():
    # in terms of formatting, the layout to the RIGHT of the = sign looks like a 2-line GUI (ignore the layout +=
    layout =  [[sg.Button(i) for i in range(4)]]
    layout += [[sg.OK()]]               # this row is better than, but is the same as
    layout.append([sg.Cancel()])        # .. this row in that they both add a new ROW with a button on it

    window = sg.Window('Generated Layouts', layout)

    event, values = window.Read()

    print(event, values)
    window.Close()


"""
    Construct 4 - Using + to place Elements on the same row
    
    If you want to put elements on the same row, you can simply add them together.  All that is happening is that the
    items in one list are added to the items in another.  That's true for all these contructs using +
"""

def layout4():
    layout =  [[sg.Text('Enter some info')] + [sg.Input()] + [sg.Exit()]]

    window = sg.Window('Generated Layouts', layout)

    event, values = window.Read()

    print(event, values)
    window.Close()


"""
    Construct #5 - Simple "concatenation" of layouts
    Layouts are lists of lists.  Some of the examples and demo programs use a .append method to add rows to layouts.
    These will soono be replaced with this new technique.  It's so simple that I don't know why it took so long to
    find it.
    This layout uses list comprehensions heavily, and even uses 2 of them. So, if you find them confusing, skip down
    to the next Construct and you'll see the same layout built except for loops are used rather than comprehensions
    
    The next 3 examples all use this same window that is layed out like this:
        Each row is:
    Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
    Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
    ...
    
    It shows, in particular, this handy bit of layout building, a += to add on additional rows.
    layout =  [[stuff on row 1], [stuff on row 2]]
    layout += [[stuff on row 3]]
    
    Works as long as the things you are adding together look like this [[ ]]  (the famous double bracket layouts of PSG)
"""

def layout5():
    questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?',
                 'Get along with people in your family?', 'Get along with people outside your family?',
                 'Get along well in social situations?', 'Feel close to another person',
                 'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?')

    layout = [[sg.T(qnum + 1, size=(2, 2)), sg.T(q, size=(30, 2))] + [sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, col)) for col in range(5)] for qnum, q in enumerate(questions)]
    layout += [[sg.OK()]]

    window = sg.Window('Computed Layout Questionnaire', layout)
    event, values = window.Read()

    print(event, values)
    window.Close()


"""
    Construct #6 - Computed layout without using list comprehensions
    This layout is identical to Contruct #5.  The difference is that rather than use list comprehensions, this code
    uses for loops.  Perhaps if you're a beginner this version makes more sense?

    In this example we start with a "blank layout" [[ ]] and add onto it.

    Works as long as the things you are adding together look like this [[ ]]  (the famous double bracket layouts of PSG)
"""


def layout6():
    questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?',
                 'Get along with people in your family?', 'Get along with people outside your family?',
                 'Get along well in social situations?', 'Feel close to another person',
                 'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?')

    layout = [[]]
    for qnum, question in enumerate(questions):     # loop through questions
        row_layout = [sg.T(qnum + 1, size=(2, 2)), sg.T(question, size=(30, 2))]    # rows start with # and question
        for radio_num in range(5):                  # loop through 5 radio buttons and add to row
            row_layout += [sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, radio_num))]
        layout += [row_layout]                      # after row is completed layout, tack it onto the end of final layout

    layout += [[sg.OK()]]                           # and finally, add a row to the bottom that has an OK button

    window = sg.Window('Computed Layout Questionnaire', layout)
    event, values = window.Read()

    print(event, values)
    window.Close()



"""
    Construct #7 - * operator and list comprehensions 
        Using the * operator from inside the layout
        List comprehension inside the layout
        Addition of rows to layouts
        All in a single variable assignment
        
    NOTE - this particular code, using the * operator, will not work on Python 2 and think it was added in Python 3.5
    
    This code shows a bunch of questions with Radio Button choices
    
    There is a double-loop comprehension used.  One that loops through the questions (rows) and the other loops through
    the Radio Button choices.
    Thus each row is:
    Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
    Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
    Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5
    
    What the * operator is doing in these cases is expanding the list they are in front of into a SERIES of items
    from the list... one after another, as if they are separated with comma.  It's a way of "unpacking" from within
    a statement.
    
    The result is a beautifully compact way to make a layout, still using a layout variable, that consists of a
    variable number of rows and a variable number of columns in each row.
"""

def layout7():
    questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?',
                 'Get along with people in your family?', 'Get along with people outside your family?',
                 'Get along well in social situations?', 'Feel close to another person',
                 'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?')

    layout = [[*[sg.T(qnum + 1, size=(2, 2)), sg.T(q, size=(30, 2))], # These are the question # and the question text
               *[sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, col)) for col in range(5)]] for qnum, q in enumerate(questions)] + [[sg.OK()]]     # finally add an OK button at the very bottom by using the '+' operator

    window = sg.Window('Questionnaire', layout)

    event, values = window.Read()

    print(event, values)
    window.Close()


# ------------------------- Call each of the Constructs -------------------------

layout0()
layout1()
layout2()
layout3()
layout4()
layout5()
layout6()
layout7()
@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 18, 2019

More Fun Collapsing Code - Replacing Update call with.... nothing?

An bit of alien syntax has been added to PySimpleGUI. By no means do you have to use it, but you should understand that it exists and that you may run across it. The problem is that beginners won't understand it..... and I'm sure to use it a fair amount because it removes TWO function names from a statement.

Recall that window.FindElement(key) was replaced by window[key]

Typically the reason you're finding the element is that you want to call the element's Update method. When examined to see if it can be collapsed / simplified a rather strange and wonderful design pattern emerged.

Here's a little demo that will replace the text on the first line with whatever you type into the input box.

import PySimpleGUI as sg

layout = [[sg.Text('My layout', key='T')],
            [sg.In(key='I')],
            [sg.Button('Go')]]

window = sg.Window('My new window', layout)

while True:             # Event Loop
    event, values = window.Read()
    print(event, values)
    if event is None: break

    window.FindElement('T').Update(values['I'])
window.Close()

The statement to focus on is this one:
window.FindElement('T').Update(values['I'])

You already know the FindElement trick, now it's time for the Update trick. This is so alien..... Here goes...

Instead of getting the element and then calling Update, you "call the element". Yea, sounds weird.

Replace this line
window.FindElement('T').Update(values['I'])

with this one
window['T'](values['I'])

You are deleting .Update from your statement. What's left is just the (parms) portion. It looks foreign to me, but I can sure get used to it!

Of course, if you a variable that contains an element, you can "call" that variable.

text_elem = sg.Text('My text')

text_elem('new text')      # calls the Update method for that element

MMMmmmm mmmmm that's some mighty tasty Python there!

I'm stunned daily at what this language can do and what ideas users have.

So if you see this kind of thing, you'll know what it is. I'll be sure and comment them anyway.

This change is already on GitHub and will be release to PyPI tonight

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 19, 2019

YouTube Tutorial That Includes PySimpleGUI GUI

I spotted a new video on YouTube that uses PySimpleGUI.
https://www.youtube.com/watch?v=IWDC9vcBIFQ

He did a nice thing by adding an index in the description to the location it can be found in the video. He's using it in an OO design, so it was really interesting to see how he split things up.

I learn something every time I see someone else's code.

It was nice to hear some kind words about PySimpleGUI. No complaints at all. Just reinforcement that it's simple.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 19, 2019

Release PySimpleGUI 4.2 and PySimpleGUI27 2.2

The cool lookup release! No more need for FindElement. You can continue to use FindElement.
However, your code will look weird and ancient. ;-) (i.e. readable)
MORE Docstring and main doc updates!

  • New window[key] == window.FindElement(key)
  • New Update calling method. Can directly call an Element and it will call its Update method
    • windowkey == window.FindElement(key).Update(value=new_value)
  • Made Tearoff part of element so anything can be a menu in theory
  • Removed a bunch of del calls. Hoping it doesn't bite me in memory leaks
  • Combo.Get method added
  • Combo.GetSelectedItemsIndexes removed
  • New Graph methods SendFigureToBack, BringFigureToFront
  • Butten release changed for better Graph Dragging
    • Now returns key+"Up" for the event
    • Also returns the x,y coords in the values
  • Tab.Select method added
  • TabGroup.Get method added - returns key of currently selected Tab
  • Window finalize parameter added - Will call finalize if a layout is also included. No more need for Finalize!!
  • Quiet, steady change to PEP8 user interface started
    • Now available are Window methods - read, layout, finalize, find_element, element, close
    • Should provide 100% PEP with these alone for most PySimpleGUI programs
  • Added finding focus across ALL elements by using the .Widget member variable
  • Fixed sizing Columns! NOW they will finally be the size specified
  • Fixed not using the initialdir paramter in PopupGetFile if the no_window option is set
@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 19, 2019

Coming soon.... PEP8 compliant interfaces

The latest 4.2 release has with it these new Window methods:
read = Read
layout = Layout
finalize = Finalize
find_element = FindElement
element =FindElement
close = Close

Starting with Windows as these methods are what tend to be called the most. The Update methods for the elements are the 2nd most called and they can be dropped completely now by directly calling the element. I'll still add all the update methods, don't worry.

I hope to have a bunch more done this week. So, if TheCamelCaseMethod and CamelCaseFuncton names are driving you crazy, then don't worry, a fix is otw.

You can call:
window.read()
in this latest 4.2 release as well as
window.close()

That should take care of 70% of the PySimpleGUI programs out there.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 21, 2019

PEP8 Compliant Interfaces for PySimpleGUI is Done!

The tkinter port of PySimpleGUI now has PEP8 named method and function names. All of the user accessable methods and functions have a new PEP8 compliant name in addition to the original names.

In other words, all of your old code continues to operate as it did before. You could even mix them if you wanted. Maybe you like the way Popup looks instead of popup. Take your pick.

Assuming all goes well, you can expect this to be cross-ported quickly and released to PyPI.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 21, 2019

Nice Tweet....

I go surfing for comments from time to time, just to see how the world is liking or hating PySimpleGUI. Thankfully, 99% of the comments I see posted are positive. Only recently have I taken a moment to notice that this is happening on a daily basis more and more.

Twitter I don't check as often as StackOverflow for example, but I do check it on occassion. Tonight I was treated to this surprising tweet:

SNAG-0453

And, no, I didn't pay him to say "after reading the docs", but I did thank him of course.

You guys/gals are the best user community on GitHub!

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 21, 2019

Window gets finalize parameter

The layout for a window used to require a chained call or a plain call to Window.Layout. A few weeks ago, the layout became a parameter to the Window initialization, thus removing the need to call Layout directly.

The next on the hit-list to remove is the Finalize call.

It took now have a parameter finalize that you can set to True if you want the window to be finalized when it's created.

The old way:

window = window('Title').Layout(layout).Finalize()

The new way:

window = window('Title', layout, finalize=True)

It will do the same thing without the chaining, which is really foreign to beginners.

Like all changes to the SDK, the old interfaces will remain for quite a while, but may eventually be phased out after all documentation and demo programs have been modified to remove those calls.

@MikeTheWatchGuy

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 22, 2019

Legacy Python (2.7) Users Need to Begin Planning

As is discussed in the documentation, the Python 2.7 version of PySimpleGUI will cease to exist on Jan 1 2020. There will be no copes on this GitHub. The final 2.7 release will be left up on PyPI, but no new additions nor patches will be made.

"Cease to exist" = rm PySimpleGUI27*

In the coming weeks the final PySimpleGUI27.py file will be frozen, hopefully at a nice place in terms of the features you want.After the freeze, there will be no updates passed over from the PySimpleGUI.py work.

@PySimpleGUI

This comment has been minimized.

Copy link
Owner

commented Aug 22, 2019

New element_justification parameter for Window and Container Elements

You'll find the latest (on GitHub) PySimpleGUI.py file has the ability to do 2 new things.

Sizer Element

Add this element to a container and it will pad the container out to the specified size. It can be in 1 direction as well (i.e. only pad horizontally)

element_justification Parameter added to Window, Column, Tab and Frame

Valid values are 'left', 'right', 'center'
You only need to specify the first letter
element_justification = 'c'

The default, of course, is left justification.

@PySimpleGUI

This comment has been minimized.

Copy link
Owner

commented Aug 22, 2019

4.3 PySimpleGUI Release 22-Aug-2019

PEP8 PEP8 PEP8
Layout controls! Can finally center stuff
Some rather impactful changes this time
Let's hope it doesn't all blow up in our faces!

  • PEP8 interfaces added for Class methods & functions
    • Finally a PEP8 compliant interface for PySimpleGUI!!
    • The "old CamelCase" are still in place and will be for quite some time
    • Can mix and match at will if you want, but suggest picking one and sticking with it
    • All docs and demo programs will need to be changed
  • Internally saving parent row frame for layout checks
  • Warnings on all Update calls - checks if Window.Read or Window.Finalize has been called
  • Warning if a layout is attempted to be used twice
    • Shows an "Error Popup" to get the user's attention for sure
  • Removed all element-specific SetFocus methods and made it available to ALL elements
  • Listbox - no_scrollbar parameter added. If True then no scrollbar will be shown
  • NEW finalize bool parameter added to Window. Removes need to "chain" .Finalize() call.
  • NEW element_justification parameter for Column, Frame, Tab Elements and Window
    • Valid values are 'left', 'right', 'center'. Only first letter checked so can use 'l', 'c','r'
    • Default = 'left'
    • Result is that all Elements INSIDE of this container will be justified as specified
    • Works well with new Sizer Elements
  • NEW justification parameter for Column elements.
    • Justifies Column AND the row it's on to this setting (left, right, center)
    • Enables individual rows to be justified in addition to the entire window
  • NEW Sizer Element
    • Has width and height parameters. Can set one or both
    • Causes the element it is contained within to expand according to width and height of Sizer Element
    • Helps greatly with centering. Frames will shrink to fit the contents for example. Use Sizer to pad out to right size
  • Added Window.visibility_changed to match the PySimpleGUIQt call
  • Fixed Debugger so that popout window shows any newly added locals
@PySimpleGUI

This comment has been minimized.

Copy link
Owner

commented Aug 22, 2019

More Twitter?

Never thought about Twitter being a place where PySimpleGUI would be discussed.

But more and more posts are showing up. Here's a nice one from today:

image


#PySimpleGUI is something positive it would appear. What a contrast from other sites.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants
You can’t perform that action at this time.