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()
Related
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()
I'm building a simple GUI to try to gain some familiarity with PySimpleGUI. I started by working on a geocoder, and everything else in the GUI works except for a single message in a Status Bar element. This element should show 'Geocoding, please wait...' (or similar string) before running the geocoding function, but this doesn't occur.
The relevant code snippet is below:
def makelayout():
import PySimpleGUI as sg
import pandas as pd
import threading
sg.theme("Reddit")
layout = [
[sg.T("")],
[sg.Text("Choose a file: ",size=(28,1)),sg.Input(enable_events=True,key="-INPUT-",size=(40,1)),sg.FileBrowse(pad=(5,0),file_types=(("XLSX",".xlsx"),))],
[sg.Text("Choose the column to be geocoded: ",size=(28,1)),sg.Combo(enable_events=True,values=[],key="-HEADER-",size=(40,1))],
[sg.Text("Choose an output location & name: ",size=(28,1)),sg.Input(size=(40,1)),sg.FileSaveAs(pad=(5,0),size=(15,1),key="-OUT-",file_types=(("CSV",".csv"),))],
[sg.Button("Submit",size=(12,1)),sg.Checkbox("Remove duplicates?",default=False,key="-CHECK-")],
[sg.StatusBar("",size=(100,1),key="-STAT-")]]
while True:
event,values = window.read()
if event == sg.WIN_CLOSED or event == "Exit":
break
elif event == "-INPUT-":
header_list = getheaders(values["-INPUT-"])
window["-HEADER-"].update(values=header_list)
elif event == "Submit":
try:
if values['-CHECK-']:
df = pd.read_excel(values["-INPUT-"])
df = df.drop_duplicates()
else:
df = pd.read_excel(values["-INPUT-"])
if values['-HEADER-'] != '' and values['-OUT-'] != '':
print("Header & Output are not empty") #This prints out w/o an issue.
window['-STAT-'].update("Geocoding, please wait...") #Not showing. Why?
id1 = threading.Thread(target=search,args=(df[values['-HEADER-']],str(values['-OUT-'])))
search(df[values['-HEADER-']],str(values['-OUT-']))
if not id1.is_alive():
window['-STAT-'].update("Finished geocoding! Please check {}".format(values['-OUT-']))
except:
window['-STAT-'].update('Error. Invalid and/or missing inputs detected.')
Oddly enough, the other updates to the status bar (in the except clause for instance) work without an issue. Any ideas what i'm missing?
I am creating a search engine based on this Youtube tutorial which gives the output of the search result in a sg.Output element. I want each result to be clickable and open in Windows File Explorer with the file selected.
My issues is in a PySimpleGUI output box (sg.Output) I can only seem to have text.
How can I have text with a link attached to run a subprocess like this? My guess is it is something like what was discussed here:
sg.Text('Here', is_link=True, key='link') # then link the key to an event
However, as previously mentioned, if I add anything but text to sg.Output it does not work, i.e., the following does not work:
sg.window.FindElement('-OUTPUT-').Update(sg.Text('Here', is_link=True, key='link'))
It will be much complex to enable hyperlink function for sg.Output or sg.Multiline.
Here's simple code to provide hyperlink function for sg.Text, also work for some other elements, by using options enable_events=True and tooltip.
import webbrowser
import PySimpleGUI as sg
urls = {
'Google':'https://www.google.com',
'Amazon':'https://www.amazon.com/',
'NASA' :'https://www.nasa.gov/',
'Python':'https://www.python.org/',
}
items = sorted(urls.keys())
sg.theme("DarkBlue")
font = ('Courier New', 16, 'underline')
layout = [[sg.Text(txt, tooltip=urls[txt], enable_events=True, font=font,
key=f'URL {urls[txt]}')] for txt in items]
window = sg.Window('Hyperlink', layout, size=(250, 150), finalize=True)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event.startswith("URL "):
url = event.split(' ')[1]
webbrowser.open(url)
print(event, values)
window.close()
This took awhile, however I'm sure I'll use it a lot. xD
Additional you can specify the tooltip parameter to show the site it'll go to.
import PySimpleGUI as sg
import webbrowser
layout = [
[
sg.Text("My Awesome Link", text_color="#0000EE", font=(None, 20), enable_events=True, key="-LINK-")
]
]
window = sg.Window("Hyperlink", layout, finalize=True)
window["-LINK-"].set_cursor("hand2")
window["-LINK-"].Widget.bind("<Enter>", lambda _: window["-LINK-"].update(font=(None, 20, "underline")))
window["-LINK-"].Widget.bind("<Leave>", lambda _: window["-LINK-"].update(font=(None, 20)))
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == "-LINK-":
webbrowser.open("https://www.pysimplegui.org/")
window.close()
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.
I am brand new with PySimpleGUI but it's turning out to be really easy as advertised. After just a couple of hours I already have a halfway-working application.
I'm using a Listbox to display several rows of items read in from a disk file. When I click my Show Connections button it reads the file and displays the items like I want. But if I click the button again, it reads the file again and now I have two copies in the box. I want to empty out the listbox before it gets updated from the next disk file read so that it always shows just exactly what is in the file. I have tried Update and set_value but can't seem to get anything to work.
layout_showdata = [
[
sg.Text('Time',size=(10,1)),
sg.Text('Destination',size=(14,1)),
sg.Text('Source',size=(14,1))],
[sg.Listbox(size=(38,10),values=[''],key='_display_')]
]
.
.
.
if event == 'Show Connections':
print('Show Connections')
window['panel_show'].update(visible=True)
window['panel_edit'].update(visible=False)
window['panel_entry'].update(visible=False)
window['_display_'].set_value([]) ***#<==This should do it I thought***
with open('connections.txt','r') as cfile:
schedule=csv.reader(cfile,dialect="pipes")
for row in schedule:
items.append(row[0]+':'+row[1]+':'+row[2]+' '+row[3]+' '+row[4])
print(items[itemnum])
itemnum+=1
window.FindElement('_display_').Update(items)
I'm sure I'm missing something simple and would appreciate whatever help I can get.
[EDIT] Added some running code to illustrate what happens. Instead of the listbox clearing when the button is pushed, it just reads the file again and adds to what is already there:
import PySimpleGUI as sg
import csv
items = []
itemnum = 0
csv.register_dialect('pipes', delimiter='|')
file = [
'01|23|45|12345678|87654321',
'04|35|23|24680864|08642468',
'01|23|45|12345678|87654321',
'04|35|23|24680864|08642468',
'01|23|45|12345678|87654321',
'23|23|23|12341234|43214321'
]
layout_showdata = [
[
sg.Text('Time',size=(10,1)),
sg.Text('Destination',size=(14,1)),
sg.Text('Source',size=(14,1))],
[sg.Listbox(size=(38,10),values=[''],key='_display_')],
[sg.Button('Show Connections')]
]
window = sg.Window('XY Scheduler', layout_showdata)
while True:
event, values = window.Read(timeout=1)
if event in (None, 'Quit'):
break
#Show Existing Connections
if event == 'Show Connections':
print('Show Connections')
window['_display_'].update([])
schedule=csv.reader(file,dialect="pipes")
for row in schedule:
items.append(row[0]+':'+row[1]+':'+row[2]+' '+row[3]+' '+row[4])
print(items[itemnum])
itemnum+=1
window.FindElement('_display_').Update(items)
You want the update method if you want to change the values. You'll find it has a values parameter. The call reference document or docstrings will help you quite a bit, but the general rule of thumb to use is that update is the method to change something about an element.
You had already been calling it for the other elements. You just needed to call it for the listbox.
window['_display_'].update([])
The set_values call has this description in the docs:
"Set listbox highlighted choices"
It sets what has been selected, not the choices you have to select.
[EDIT]
Here is a full example of how to remove all entries in a listbox. Maybe I'm misunderstanding the question.
import PySimpleGUI as sg
def main():
layout = [ [sg.Text('My Window')],
[sg.Listbox([1,2,3,4,5], size=(5,3), k='-LB-')],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('Window Title', layout)
while True: # Event Loop
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Go':
window['-LB-'].update([])
window.close()
if __name__ == '__main__':
main()