nonetype object not subscriptable pysimplegui - python

I'm trying to learn python and pysimplegui at the same time. Also I am old which doesn't help!
I am writing a practice program with my 10 year old son(blind leading the blind) and am running into a problem which i cant fix.
Basically the program lets you enter how many numbers to pick from and how many numbers to pick, then calculates the odds of winning. Hit generate to randomly pick the numbers for you and Print the results to a txt file for a record of your picks.
It all works fine but when i close the window i get a nonetype error which I can't work out.
Can any of ye genius's help?
This is the offending line
n=int(values['--tn--'])
from os import close
import random
from tkinter import Scrollbar
import PySimpleGUI as sg
import datetime
import math
from time import sleep, time
from PySimpleGUI.PySimpleGUI import Open, WIN_CLOSED, main
import sys
sg.theme('Reddit')
layout = [
[sg.In(size=(5,1),k="--tn--" ) ]+[sg.Text('Enter total amount of
numbers',size=(35,1))],
[sg.In(size=(5,1),k="--pn--")]+[sg.Text('Enter how many numbers
you are picking',size=(35,1))],
[sg.Text('Win odds')]+[sg.ML(background_color='light
coral',text_color='white',key='--oddout--',size=(50,2))],
[sg.ML(size=(20,30), key='--main--')],
[sg.Submit('Odds',key='--odds--')]+[sg.Submit('Generate',key='--
gen--')]+ [sg.Cancel('Cancel')]+[sg.Save(key='--save--')]+
[sg.CloseButton('Close',pad=(100,0))]
]
window = sg.Window('Lotto number generator',layout)
while True:
event, values = window.read()
n=int(values['--tn--'])
rr=int(values['--pn--'])
nf = math.factorial(n)
rf = math.factorial(rr)
winodds = (nf/(rf*math.factorial(n-rr)))
winodds = int(winodds)
now = datetime.datetime.now()
if event == WIN_CLOSED:
window['--tn--'].update('1')
break
if event == '--gen--':
r = random.sample(range(1,n),rr)
for i in r:
window['--main--'].print(i)
if event == '--odds--':
window['--oddout--'].print("Your chances of winning are
",f'{winodds:,d}', " to 1, Good Luck")
if event == 'Cancel':
window['--oddout--'].update('')
window['--tn--'].update('')
window['--pn--'].update('')
if event == '--save--':
sys.stdout = open("lotto.txt", "w")
print(values['--main--'])
sys.stdout=close(fd=0)
window.close()

event, values = window.read() is returning None. None['--tn--'] does not exist as it doesn't make sense for None to have a property, hence the error message. You have used the test to avoid this but moved it below an attempt to use the missing property. Hence the error.
It's also worth using a linting tool prompt you to make adjustments to syntax that will break your code and good practice warnings. I use pylint and flake8. The following addresses your specific error message with some tidying for the linter messages. There are still some warnings - good learning exercise :).
"""Learning program."""
from os import close
import random
import PySimpleGUI as sg
import datetime
import math
from PySimpleGUI.PySimpleGUI import Open, WIN_CLOSED, main
import sys
sg.theme('Reddit')
layout = [
[sg.In(size=(5, 1), k="--tn--")] +
[sg.Text('Enter total amount of numbers', size=(35, 1))],
[sg.In(size=(5, 1), k="--pn--")] +
[sg.Text('Enter how many numbers you are picking', size=(35, 1))],
[sg.Text('Win odds')] +
[sg.ML(
background_color='light coral', text_color='white', key='--oddout--', size=(50, 2)
)],
[sg.ML(size=(20, 30), key='--main--')],
[sg.Submit('Odds', key='--odds--')] +
[sg.Submit('Generate', key='--gen--')] +
[sg.Cancel('Cancel')] +
[sg.Save(key='--save--')] +
[sg.CloseButton('Close', pad=(100, 0))]
]
window = sg.Window('Lotto number generator', layout)
while True:
event, values = window.read()
# Moved the next three lines up and commented update which also errors
if event == WIN_CLOSED:
# window['--tn--'].update('1')
break
n = int(values['--tn--'])
rr = int(values['--pn--'])
nf = math.factorial(n)
rf = math.factorial(rr)
winodds = (nf/(rf*math.factorial(n-rr)))
winodds = int(winodds)
now = datetime.datetime.now()
if event == '--gen--':
r = random.sample(range(1, n), rr)
for i in r:
window['--main--'].print(i)
if event == '--odds--':
window['--oddout--'].print(
"Your chances of winning are", f'{winodds:,d}', " to 1, Good Luck"
)
if event == 'Cancel':
window['--oddout--'].update('')
window['--tn--'].update('')
window['--pn--'].update('')
if event == '--save--':
sys.stdout = open("lotto.txt", "w")
print(values['--main--'])
sys.stdout = close(fd=0)
window.close()
Flake8 in particular will prompt you to follow practices that don't have an obvious practical purpose. Later as you use more of the language the benefit of flake8 prompts are good habits that eventually pay large benefits.

There're something not good,
You should check the window close event first, not to processing event, values for other cases first, like following code. You may get event, values as None, None if not, then values['--tn--'] will be same as None['--tn--']. That's why you got TypeError: 'NoneType' object is not subscriptable.
while True:
event, values = window.read()
if event in (sg.WINDOW_CLOSED, 'Close'):
break
# process other events from here
window.close()
In your input fields, values['--tn--'] or values['--pn--'] maybe not with correct format for integer number, so following code may get failure ValueError: invalid literal for int() with base 10
n=int(values['--tn--'])
rr=int(values['--pn--'])
Here's my way to avoid issue,
def integer(string):
try:
value = int(string)
except:
value = None
return value
for string in ("10.5", "", "10"):
value = integer(string)
if value is None:
print(f"{repr(string)} is not a legal integer string !")
else:
print(f"{repr(string)} converted to {value} !")
'10.5' is not a legal integer string !
'' is not a legal integer string !
'10' converted to 10 !
Basically, window destroied after you click close button X of window, so you should not update anything on it.
if event == WIN_CLOSED:
# window['--tn--'].update('1')
break

When you close a window, event and values are not set, see my example below.
While debugging, it's a good practice to print out the current values of event and values to be able to check whether you get what you thought you'd get, like this:
def test():
layout = [[sg.In(size=(5, 1), k="--tn--"), sg.Text('Enter total amount of numbers', size=(35, 1))],
[sg.In(size=(5, 1), k="--pn--"), sg.Text('Enter how many numbers you are picking', size=(35, 1))],
[sg.Text('Win odds'),
sg.ML(background_color='light coral', text_color='white', key='--oddout--', size=(50, 2))],
[sg.ML(size=(20, 30), key='--main--')],
[sg.Submit('Odds', key='--odds--'), sg.Submit('Generate', key='--gen--'),
sg.Cancel('Cancel'), sg.Save(key=' - -save - -'), sg.CloseButton('Close', pad=(100, 0))]
]
window = sg.Window('Lotto number generator', layout)
while True:
event, values = window.read()
print(f'event = {event}, values = {values}')
if event == WIN_CLOSED:
break
window.close()
When you close the window, you get
event = None, values = {'--tn--': None, '--pn--': None, '--oddout--': None, '--main--': None}
so, it is important to start your main loop with if event == WIN_CLOSED: (and break the loop in that case). Only after that, you can go on to process various events and values.

Related

Is it possible to secure Tabs in my PySimpleGUI code?

Dears,
Is it possible to secure Tabs in my PySimpleGUI code ? Means that only 1st Tab can be kept accessible and the other ones request password:
Knowing that I'm able to do that using Collapsible function as follows :
def Collapsible(layout, key, title='', arrows=(sg.SYMBOL_DOWN, sg.SYMBOL_UP),
collapsed=False):
return sg.Column([[sg.T((arrows[1] if collapsed else arrows[0]), enable_events=True,
text_color='DeepSkyBlue2', k=key+'-BUTTON-'),
sg.T(title, enable_events=True, text_color='yellow', key=key+'-TITLE-')],
[sg.pin(sg.Column(layout, key=key, visible=not collapsed,
metadata=arrows))]], pad=(0,0))
==> Here's teh Layout Part
#### 1st part ####
[Collapsible(Menu1, SEC1_KEY,, collapsed=True)],
#### 2nd part ####
[Collapsible(Menu2, SEC2_KEY, collapsed=True)],
while True: # Event Loop
event, values = window.read()
#print(event, values)
if event == s
if event.startswith(SEC2_KEY):
window[SEC2_KEY].update(visible=not window[SEC2_KEY].visible)
else:
window[SEC2_KEY+'-BUTTON-'].update(window[SEC2_KEY].metadata[0] if
window[SEC2_KEY].visible else window[SEC2_KEY].metadata[1])
Any one can help on that ? Thanks
Information for a question here, IMO, it will be better.
Add everything required
Remove everything not related.
Most simple layout if GUI required.
Here, just for how to set which tab accessible. tkinter code required here.
import PySimpleGUI as sg
accessible = [0, 3]
layout = [[sg.TabGroup([[sg.Tab(f'TAB {i}',[[sg.Text(f'This is the Tab {i}')]],key=f'Tab {i}',) for i in range(5)]], key='TabGroup')]]
window = sg.Window('Tab Group', layout, finalize=True)
window['TabGroup'].bind('<Button-1>', ' Change', propagate=False)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == 'TabGroup Change':
e = window['TabGroup'].user_bind_event
if e.widget.identify(e.x, e.y) == 'label':
index = e.widget.index(f'#{e.x},{e.y}')
if index in accessible:
window[f'Tab {index}'].select()
""" Password secured
else:
if sg.popup_get_text("Password") == 'Hello':
window[f'Tab {index}'].select()
"""
window.close()

FilesBrowse() function takes all selected files as one item

I'm working on a GUI using PySimpleGUI, and I want user to browse multiple files, put each of them in an array, and get the length of the array.
But FilesBrowse() taking all files as one object and len(posts) code giving the output of "1", even If I choose 10 files.
How to fix this?
Here's my code:
import PySimpleGUI as sg
global posts
posts = []
layouts = [[sg.Text("Browse the XLSX file: "), sg.FilesBrowse(key=0)],
[sg.Button('Start The Process')], [[sg.Button('Exit')]]]
window = sg.Window("Title", layouts)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
posts.append(values[0])
x = len(posts)
print(x)
First you should see what you get print(values[0]) - and you should see that it uses ; between names - so you can split it with split(';')
posts = values[0].split(';')
or if you want to extend existing list then use += (instead of append())
posts += values[0].split(';')
Full working code
import PySimpleGUI as sg
posts = [] # it is global variable and it doesn't need `global posts`
layouts = [
[sg.Text("Browse the XLSX file: "), sg.FilesBrowse(key=0)],
[sg.Button('Start The Process')],
[sg.Button('Exit')]
]
window = sg.Window("Title", layouts)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
# --- after loop ---
if values[0]: # check if files were selected
posts += values[0].split(';')
print('len:', len(posts))
print('posts:', posts)
EDIT:
It seems on some systems if has to be inside while
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
if values[0]: # check if files were selected
posts += values[0].split(';')
print('len:', len(posts))
print('posts:', posts)
You may get problem if you click on close button of window.
It is caused by the value of values returned from window.read(), most of time, it will be None, so you cannot get values[0], then
TypeError: 'NoneType' object is not subscriptable
To update other element, sg.Filesbrowse return str and use sg.BROWSE_FILES_DELIMITER, or ';', as delimiter. So you can get a list of files by using method str.split(';'). Of course, you can call sg.popup_get_file in event loop, it may return str or list of str which depend on option multiple_files.
Demo code here
import PySimpleGUI as sg
sg.theme('DarkBlue3')
sg.set_options(element_padding=(3, 3))
posts = []
layouts = [
[sg.Text("Browse the XLSX file: ")],
[sg.Input(key='-INPUT-'), sg.FilesBrowse(file_types=(("xlsx Files", "*.xlsx"), ("ALL Files", "*.*")))],
[sg.Button('Add'), sg.Button('Exit')],
[sg.Multiline(size=(80, 25), key='-MULTILINE-')],
]
window = sg.Window("Title", layouts, finalize=True)
multiline = window['-MULTILINE-']
window['-INPUT-'].expand(expand_x=True)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == 'Add':
path = values['-INPUT-']
if path:
posts += path.split(";")
multiline.print(f'len: {len(posts)}')
multiline.print(f'posts: {posts}')
window.close()

How can I make PySimpleGui read my input and update my window?

I am not sure if this has been answered before, sorry for a duplicate if it was, but I couldn't find it anywhere clearly.
I am making a GUI for my simple AIML chatbot (entertainment purposes mostly)
and I found PySimpleGui. I read the whole documents of it and been trying to use their code, implementing it into my own small code I got from a tutorial.
Originally:
kernel = aiml.Kernel()
kernel.learn("std-startup.xml")
kernel.respond("load aiml b")
while True:
input_text = input("You: ")
response = kernel.respond(input_text)
print("Csigusz Foxoup (bot): "+response)
I got this code working, all good (Thanks Misbah)
And I got my bot to say some words in the cmd accurately.
Next I wanted to add a simple gui.
I'd much rather it look more chatty but all I could come up with with my lacking coding experince is a simple window with 2 buttons and 2 texts.
The cood looks like this:
import aiml
import PySimpleGUI as sg
kernel = aiml.Kernel()
kernel.learn("std-startup.xml")
kernel.respond("load aiml b")
sg.theme('LightBlue 1')
layout = [[sg.Text('You: '), sg.Text(size=(12,1), key='-mytext-')],
[sg.Text('Csigusz Foxoup (bot): '), sg.Text(size=(12,1), key='-CSI-')],
[sg.Input(key='-myinput-')],
[sg.Button('Send message'), sg.Button('Bye!')]]
window = sg.Window('Csigusz Foxoup, your friend in a box (bot)', layout, [200,400])
while True:
event = window.read()
values = window.read()
if event == sg.WIN_CLOSED or event == 'Bye!':
break
if event == 'Send message':
# change the "output" element to be the value of "input" element
input_text = (values)
response = kernel.respond(input_text)
window['-mytext-'].update(values['-myinput-'])
print("Csigusz Foxoup(bot): "+response)
window.close()
And it produces a nice little window for me. looks like this
My problem is that when I type something, and click the buttons, nothing happens. When I press close window (X) I get an error message saying: "You have tried 100 times to read a closed window, you need to add a check for event == WIN_CLOSED, ERROR"
Now since i have a check, also a button, I have no idea why it doesnt work. Also don't know how I could get the button to send my bot the user text then retrieve the bot output.
What Am I doing wrong? Thank you for all replies in advance. All help greatly appreciated! 🙏
All your problem is that you use .read() in wrong way.
You have to use only one .read() which returns both values as tuple (event, values)
event, values = window.read()
print('event:', event)
print('values:', values)
Minimal working code (without aiml)
import PySimpleGUI as sg
sg.theme('LightBlue 1')
layout = [[sg.Text('You: '), sg.Text(size=(50,1), key='-mytext-')],
[sg.Text('Csigusz Foxoup (bot): '), sg.Text(size=(50,1), key='-CSI-')],
[sg.Input(key='-myinput-')],
[sg.Button('Send message'), sg.Button('Bye!')]]
window = sg.Window('Csigusz Foxoup, your friend in a box (bot)', layout, [200,400])
while True:
event, values = window.read()
print('event:', event)
print('values:', values)
if event == sg.WIN_CLOSED or event == 'Bye!':
break
if event == 'Send message':
input_text = values['-myinput-']
response = "some response for " + input_text
#response = kernel.respond(input_text)
window['-mytext-'].update(input_text)
window['-CSI-'].update(response)
window.close()

How to update a GUI window in Python?

My goal is to have a window with the latest quote of a stock updating during the day. I chose alpha_vantage as a quote source, pysimplegui to create the window and twisted to run a loop to update the window every minute. The code works as written, prints the correct quote and change, creates the window as desired, but the window does not update.
Why doesn't the window update?
from alpha_vantage.timeseries import TimeSeries
from twisted.internet import task, reactor
import PySimpleGUI as sg
def paintQuote():
quote, quote_meta = av.get_intraday(symbol='spy', interval = '1min')
last = quote.iloc[-1][3]
print('{0:6.2f}'.format(last))
change = (last / yesterday - 1) * 100
print('{0:4.2f}%'.format(change))
event, values = window.read()
window['quote'].update(last)
# window color
sg.theme('BluePurple')
# window layout
layout = [[sg.Text('last price', size=(20, 2), justification='center')],
[sg.Text(''), sg.Text(size=(24,1), key='quote')]]
# create window
window = sg.Window('MikeQuote', layout)
wait = 60.0
av = TimeSeries(key ='your_key', output_format = 'pandas')
yest, yest_meta = av.get_daily(symbol='spy')
yesterday = yest.iloc[-2][3]
loop = task.LoopingCall(paintQuote)
loop.start(wait)
reactor.run()
window.close()
Answer:
Your script is not calling paintQuote more than once. Add print lines in there and you'll see it never calls it more than once.
Suggested solutions:
I don't know much about that reactor or loopingCall thing or how it works. A simpler solution is just to use a while loop with a sleep in it. Here is my solution that seemed to work well:
import PySimpleGUI as sg
from alpha_vantage.timeseries import TimeSeries
import time
sg.theme('BluePurple')
layout = [[sg.Text('Last Price', size=(20, 2), justification='center')],
[sg.Text('', size=(10, 2), font=('Helvetica', 20),
justification='center', key='quote')]]
window = sg.Window('MikeQuote', layout)
av = TimeSeries(key = 'key')
spy, _ = av.get_quote_endpoint(symbol='SPY')
last = spy['05. price']
yest = spy['08. previous close']
wait = 1 # Wait is in seconds
while True:
event, values = window.read(timeout=10)
if event in (None, 'Quit'):
break
spy, _ = av.get_quote_endpoint(symbol='SPY')
last = spy['05. price']
window['quote'].update(last)
time.sleep(wait)
I added a few tweaks including:
Calling just the "GLOBAL_QUOTE" endpoint (so you're not returning the entire massive intraday dataset)
Remove twisted package for a simple while loop with a time.sleep function.
Added a 'Quit' event so it actually stop when you close the window.
Removed the paintQuote() function. I think clean code ideally would have this function not removed, but you can add it back in however you like.
Removed the pandas integration. You're not dealing with massive data manipulation so it's easier and faster to just use the JSON format.

How To Read File Input In pySimpleGUI Then Pass It On To A Number-Crunching Processor

I would like to take input from pySimpleGUI, feed it into a normal Python var, then feed it into a music processor as I love music.
I had already tried to use wxPython for this but was unable to even get a simple fileDialog without crashing.
from pydub import AudioSegment
from os import listdir
import numpy as np
import math
import PySimpleGUI as sg
class Dankify():
song_dir = "songs"
attenuate_db = 0
accentuate_db = 2
yeet = sg.Window('Dankify ALL THE THINGS!'). Layout([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]).Read()
event, values = yeet.Read()
yeet1 = event, values
def bass_line_freq(track):
sample_track = list(track)
# c-value
est_mean = np.mean(sample_track)
# a-value
est_std = 3 * np.std(sample_track) / (math.sqrt(2))
bass_factor = int(round((est_std - est_mean) * 0.005))
return bass_factor
songfile = yeet1
for filename in listdir(songfile):
sample = AudioSegment.from_mp3(songfile)
filtered = sample.low_pass_filter(bass_line_freq(sample.get_array_of_samples()))
combined = (sample - attenuate_db).overlay(filtered + accentuate_db)
combined.export("exports/" + filename.replace(".mp3", "") + "-export.mp3", format="mp3")
However, it just does nothing, not even processing it. A reminder that I am using some open-source code and that I'm a beginner which knows nothing about how all this works and am trying to build real stuff to gain experience. Thanks!
I guess you are missing the "event loop".
Try something like this, hope it helps.
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_') ],
[sg.Input(do_not_clear=True, key='_IN_')],
[sg.Button('Show'), sg.Button('Exit')]]
window = sg.Window('Window Title').Layout(layout)
while True: # Event Loop
event, values = window.Read()
print(event, values)
if event is None or event == 'Exit':
break
if event == 'Show':
# change the "output" element to be the value of "input" element
window.FindElement('_OUTPUT_').Update(values['_IN_'])
window.Close()
You're doing 2 Read calls.
Try changing to this:
yeet = sg.Window('Dankify ALL THE THINGS!').Layout(
[[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()]])
event, values = yeet.Read()
Without the Read on the end of the first statement.
You are instantiating this class, right?
d = Dankify()

Categories