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()
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 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()
Trying to build a simple window with PySimpleGUI. I want to have a list of lines with info about videos that are stored in a list. This is my code:
layout = [
[sg.Text("Clique para iniciar:")],
*[[sg.Button(k)] for k in botoes],
# [sg.Column([[sg.Text('Passos')]], key = 'CONTAINER_STATUS')],
[sg.Text('Índice Vídeo Selecionado:'), sg.In(key=0), sg.In(key=1, enable_events=True)],
# [sg.Text('Status Vídeos: ')],
[sg.Text('Vídeo'), sg.Text('Status Vídeo'), sg.Text('Status Thumbnail')],
[sg.Column([[sg.Text('test'), sg.Text('test2')],[sg.Text('test3'),sg.Text('test4')]], key='CONTAINER_VIDEOS', size= (50, 50))],
]
window = sg.Window("Iniciar Processamento de Vídeo", layout)
def get_layout_video(video):
status = video.status.name if video.status is not None else ''
status_thumb = video.thumbnail.status.name if video.thumbnail.status is not None else ''
try:
layout = [
sg.Text(video.id_video_original), sg.Text(status), sg.Text(status_thumb)
]
return layout
except Exception as e:
print(e)
return []
# Create an event loop
print('Running')
while True:
event, values = window.read()
window["CONTAINER_VIDEOS"].update([get_layout_video(v) for v in sessao.videos] if len(sessao.videos) > 0 else [[]])
if event == sg.WIN_CLOSED:
break
I have debugged with several different possibilities. The events that cause the list to change were omitted, but I'm sure they work. The result of get_layout_video is correct, it looks like a normal layout for the column, similar to the test one. I have also tried an empty list of lists [[]], but nothing happens. The Column simply keeps it's original value. When I try another type of element (such as sg.Text) it works correctly, updating the value. What am I doing wrong?
I couldn't find how to directly change the column of the sg, but I made an update function that will solve your problem, it will close and recreate the sg window with the updated column.
Here is the code for the update column.
def update_column():
global window
layout = [
[sg.Text("Clique para iniciar:")],
*[[sg.Button(k)] for k in botoes],
# [sg.Column([[sg.Text('Passos')]], key = 'CONTAINER_STATUS')],
[sg.Text('Índice Vídeo Selecionado:'), sg.In(key=0), sg.In(key=1, enable_events=True)],
# [sg.Text('Status Vídeos: ')],
[sg.Text('Vídeo'), sg.Text('Status Vídeo'), sg.Text('Status Thumbnail')],
[sg.Column([get_layout_video(v) for v in sessao.videos] if len(sessao.videos) > 0 else [[]], key='CONTAINER_VIDEOS', size=(150, 50))],
]
window.close()
window = sg.Window("Iniciar Processamento de Vídeo", layout)
print('Running')
while True:
event, values = window.read()
update_column()
if event == sg.WIN_CLOSED:
break
some photos:
before I click anything:
after I click something:
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()