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()
Related
Trying to make the GUI application calculate cost. Unsure how to do it
So I've built my GUI Application using PySimpleGUI but i'd like to make it functional and calculate the total membership cost in the pop up window I've created. Can someone point me in the right direction? How do I apply numerical values to radio buttons and checkboxes to calculate costs?
Thanks! sorry I'm very new to this so I'm not well versed in Python.
Most of time, I use dictionary to map the item to a value for calculation later. With enable_Events=True in Checkbox or Radio element, or another Submit Button, to generate an event when click for the calculation and GUI update.
import PySimpleGUI as sg
discounts = {"75%":0.75, "85%":0.85, "95%":0.95, "No":1.0}
prices = {"Summer Dress":545, "Coat":275, "Elegant Suit":685, "Casual Shirt":98, "Bridesmaid Dress":440, "Shoes":195}
layout = [
[sg.Text("Discount")],
[sg.Radio(discount, "Discount", enable_events=True, key=("Discount", discount)) for discount in discounts],
[sg.Text("Price List")]] + [
[sg.Checkbox(price, enable_events=True, key=("Price", price)), sg.Push(), sg.Text(prices[price])] for price in prices] + [
[sg.Text("Total: 0", justification='right', expand_x=True, key='Total')],
]
window = sg.Window('Example', layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif isinstance(event, tuple) and event[0] in ('Discount', 'Price'):
dis = 1
for discount in discounts:
if values[('Discount', discount)]:
dis = discounts[discount]
break
total = sum([prices[price] for price in prices if values[("Price", price)]])
window['Total'].update(f"Total: {int(total*dis)}")
window.close()
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 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()
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()
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()