PySimpleGUI - Column won't update - python

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:

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()

Status Bar not updating PySimpleGUI

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?

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()

nonetype object not subscriptable pysimplegui

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.

How to empty out Listbox in PySimpleGUI

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()

Categories