hide_row() in pysimplegui work only one time? - python

I use hide_row() element or feature in my code to hide row but it work only one time when i connected to Retreat button or Retreat key, when i clicked again or in the 2nd time it's not work (see the code or try it to understand what i mean) how make it work continuously not only one time or is there another way to undo or delete section or row.
import PySimpleGUI as sg
layout = [
[sg.Col([[sg.T('Enter your name:'),sg.In()],[sg.Btn('Ok'), sg.Btn('Exit'),
sg.Btn('Add new row')]],k='col_key')]
]
window = sg.Window('Test', layout)
while True:
event, values = window.read()
if event in ('Exit' ,sg.WIN_CLOSED):
break
if event == ('Add new row'):
window.extend_layout(window['col_key'],[[sg.T('Enter your name:', key='new_raw'),(sg.In())]])
window.extend_layout(window['col_key'],[[sg.Btn('Retreat', key='re')]])
if event == ('re'):
window['new_raw'].hide_row()
window['re'].hide_row()
window.close()

It is caused by new element with duplicate key when add new row again, so it wll always find the first element when you call window[new_key].
Should use different key for different element when you create a new element.
Key of new element will be replace by
element.Key = str(element.Key) + str(self.UniqueKeyCounter)
So key for new elements will be
'new_raw', 're', 'new_raw0', 're1', 'new_raw2', 're3', 'new_raw4', 're5', ...
you can check it by
>>> window.key_dict
{
...
'new_raw': <PySimpleGUI.PySimpleGUI.Text object at 0x000001D3A5B80FA0>,
'new_raw0': <PySimpleGUI.PySimpleGUI.Text object at 0x000001D3A6FA1280>,
'new_raw2': <PySimpleGUI.PySimpleGUI.Text object at 0x000001D3A6FA1220>,
're': <PySimpleGUI.PySimpleGUI.Button object at 0x000001D3A6FA1CD0>,
're1': <PySimpleGUI.PySimpleGUI.Button object at 0x000001D3A6FA1340>,
're3': <PySimpleGUI.PySimpleGUI.Button object at 0x000001D3A6FA11F0>}
After new row generated, the key of button Retreat is not 're', but starts with 're' and ends with an index.
Update code as following
import PySimpleGUI as sg
layout = [
[sg.Col([[sg.T('Enter your name:'),sg.In()],[sg.Btn('Ok'), sg.Btn('Exit'),
sg.Btn('Add new row')]],k='col_key')]
]
window = sg.Window('Test', layout)
index = 0
while True:
event, values = window.read()
if event in ('Exit' ,sg.WIN_CLOSED):
break
if event == ('Add new row'):
window.extend_layout(window['col_key'],[[sg.T('Enter your name:', key=f'new_raw {index}'),(sg.In(key=f'input {index}'))]])
window.extend_layout(window['col_key'],[[sg.Btn('Retreat', key=f're {index}')]])
index += 1
if event.startswith('re'):
i = event.split(' ')[-1]
window[f'new_raw {i}'].hide_row()
window[f're {i}'].hide_row()
window.close()
There's still problem for old rows hidden will still be kept in memory, so occupied memory will grow higher and higher if you add new row again and again, but it's not question to be discussed here.

Related

table rows not showing properly, only one row is displayed after running

i have written the right code in python simple gui, but after running the code, only one row is displayed and the whole table is not shown i dont know why.this is what is displayed after running the code.
import PySimpleGUI as sg
rows=[
["CULTUS","4","3500","300","550"],
["SONATA","3","5000","350","750"],
["KIA Sportage","3","6000","500","700"],
["Yaris","5","4000","300","600"],
["HONDA CIVIC","5","5500","300","600"],
["Pajero","2","7000","500","700"]
]
header =[
["Model", "Available", "Price/Day","Liability Insurance/Day", "Comprehensive Insurance/Day"]
]
layout=[[sg.Table(
values=rows,
headings=header,
size=(500,300),
key="-TABLE-",)
],
[sg.OK(),sg.Cancel()]
]
window = sg.Window('TG Enterprises', layout ,size=(600,400))
event, values = window.read()
if event == 'Cancel':
window.close()
print(event,values)
this is my code ,can anyone please help me what is wrong with this code and what can i do to make it right?
I cut out some of the text to make it easier to comprehend, but basically it turns out that your attempt was very close.
The main error was the header was a list containing a list. whereas it should just have been a list.
so using boilerplate i made a working example for you like this:
import PySimpleGUI as sg
rows=[
["CULTUS","4","3500","300","550"],
["SONATA","3","5000","350","750"],
["KIA Sportage","3","6000","500","700"],
["Yaris","5","4000","300","600"],
["HONDA CIVIC","5","5500","300","600"],
["Pajero","2","7000","500","700"]
]
header =["Model", "Available", "Price","Liability", "Comprehensive"]
layout = [[sg.Text(x) for x in header],
[sg.Table(values=rows,
headings=header,
max_col_width=25,
auto_size_columns=True,
justification='right',
alternating_row_color='lightblue',
key='table')],
[sg.Button('Add'), sg.Button('Delete')]]
window = sg.Window('Table example').Layout(layout)
while True:
event, values = window.Read()
if event is None:
break
print(event, values)
window.Close()
and the result looks like this:
note: I didnt try out the buttons or any other logic. I just provided an example of the correct formatting example.

Update Values in PySimpleGui from external script

I am new to PySimpleGui and wonder if the following concept can be applied.
I have to work with two scripts, one related to a GUI and a second one that it is a "Do Something Script".
I am interested in printing the status of an external loop in a window of my GUI Script
The GUIScript that I have coded as an example is the following:
import PySimpleGUI as sg
import MainScript
### GUI Script ###
layout = [
[sg.Text('My text 1 is: '), sg.Text('{}'.format('foo1'), key='-text1-')],
[sg.Text('My text 2 is: '), sg.Text('{}'.format('foo2'), key='-text2-')],
[sg.Text('Sweep number: '), sg.Text('{}'.format('0'), key='-SWEEP-')],
[sg.ProgressBar(max_value=40, orientation='horizontal', size=(50,20), key='-PRO_BAR-')],
[sg.Button("OK"), sg.Button("Cancel")],
]
window =sg.Window("Progress Bar",layout)
while True:
event, values = window.read()
if event == 'Cancel' or event == sg.WIN_CLOSED:
break
if event == "OK":
for s in range(50):
event, values = window.read(1000)
if event == 'Cancel':
break
window['-PRO_BAR-'].update(s+1)
window['-text1-'].update('my new text1')
window['-text2-'].update('my new text2')
window['-SWEEP-'].update(s)
window.refresh()
window.close()
The previous script works as "what I would like to have". I mean that the window['-PRO_BAR-'].update(s+1) and window['-SWEEP-'].update(s) update according to the s value of its own for-loop.
However, I would like to pick up the variable=i from the MainScript (which is the following), and use it back in the GUIScript.
##### MainScript ####
SWEEPS = 50
SWEEPS_LEN = list(range(1, SWEEPS+1))
for i in enumerate(SWEEPS_LEN):
print('sweep: {}'.format(i))
# Do something
# Save Data
Here, it is obvious that i=0, i=1, i=2.... I want to use these variables (that change in the loop) them as:
window['-PRO_BAR-'].update(i+1) and window['-SWEEP-'].update(i), such as the values update.
The GUIScript would show:
Sweep: 1
then i=2, updates and shows
Sweep: 2
then updates and...
Thanks.

Is there a way to remove certain inputs from a window in PySimpleGUI without removing others?

I am new new to Python/PySimpleGUI, so sorry if this would be incredibly obvious. (Also, yes, this is code I am using from a Youtube tutorial, I am not trying to take any credit for the code below.)
I have a simple program using pandas, pysimplegui, and openpyxl to input a set of values from the end user (using PySimpleGUI windows) into an Excel sheet; however, there are certain fields that I want to keep constant (such as "date" being today.strftime("%m/%d/%y")). Here's how I have tried doing it:
Defining what I want constant as variables:
bkey = 'My Name'
today = date.today()
date = today.strftime("%m/%d/%y")
And then, in pysimplegui, letting the sg.inputtext() be the variables, e.g.:
[sg.Text('Entry By', size=(15,1)), sg.InputText(bkey)]
For reference, here is a window with key as the input:
[sg.Text('To Department', size=(15,1)), sg.InputText(key='To Department')]
And then in the event of user clicking "Submit" or "Clear," calling def Clear_Input():
def clear_input():
for key in values:
window[key]('')
return None
I have no idea why clear_input() is clearing ALL values in the window, because to me it is saying any values as key in the window == '', so anything NOT defined as key (such as bkey) shouldn't be cleared.
Let me know if there is any more info I can give, I'd be glad to give it. Again, newb, sorry in advance.
Here is the full source code:
import PySimpleGUI as sg
import pandas as pd
from datetime import date
#color add to window_height
sg.theme('Topanga')
EXCEL_FILE = r'C:\Users\User\Desktop\DataEntry.xlsx'
df = pd.read_excel(EXCEL_FILE)
bkey = 'My Name'
today = date.today()
date = today.strftime("%m/%d/%y")
layout = [
[sg.Text('Please fill out the following fields:')],
[sg.Text('Service Tag', size=(15,1)), sg.InputText(key='Tag')],
[sg.Text('Serial Number (EPB)', size=(15,1)), sg.InputText(key='Serial Number')],
[sg.Text('Type of Equipment', size=(15,1)), sg.Combo(['Desktop', 'Laptop', 'Monitor', 'Tablet'], key='Type of Equipment')],
[sg.Text('New/Move', size=(15,1)), sg.InputText(key='New/Move')],
[sg.Text('User\'s Name', size=(15,1)), sg.InputText(key='User\'s Name')],
[sg.Text('From Room', size=(15,1)), sg.InputText(key='From Room')],
[sg.Text('To Room', size=(15,1)), sg.InputText(key='To Room')],
[sg.Text('To Department', size=(15,1)), sg.InputText(key='To Department')],
[sg.Text('Date', size=(15,1)), sg.InputText(date)],
[sg.Text('Attached Tag', size=(15,1)), sg.InputText(key='Attached Tag')],
[sg.Text('Entry By', size=(15,1)), sg.InputText(bkey)],
[sg.Submit(), sg.Button('Clear'), sg.Exit()]
]
window = sg.Window('simple data entry form', layout)
def clear_input():
for key in values:
window[key]('')
return None
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Clear':
clear_input()
if event == 'Submit':
df = df.append(values, ignore_index=True)
df.to_excel(EXCEL_FILE, index=False)
sg.popup('Data saved!')
clear_input()
window.close()
There will be a default key generated for Input element or some other element if you don't specify the value for both of option key and option k.
Some statements updated here for you code
today = date.today()
now = today.strftime("%m/%d/%y") # date is the libray name
# In layout
[sg.Text('Date', size=(15,1)), sg.InputText(now, key='Date')], # Define the key
[sg.Text('Attached Tag', size=(15,1)), sg.InputText(key='Attached Tag')],
[sg.Text('Entry By', size=(15,1)), sg.InputText(bkey, key='Entry By')], # Define the key
# Tuple for the input elements not to clear
not_to_clear = ('Entry By', 'Date')
def clear_input():
# For elements in the window
for key, element in window.key_dict.items():
# If element is Input element and not in the not-to-clear tuple
if isinstance(element, sg.Input) and key not in not_to_clear:
element.update('')
PySimpleGui.Window.read() returns two things: event and values. event contains a description of whatever event triggered the message. values contains a dictionary of all of the input components in your layout. The key in that dictionary is the key parameter from your Input item. The value is the current value of that input item.
So, clear_input is just running through the set of Input elements in your layout, and setting their values to blank.
The key item is that names, like bkey and key have no meaning at runtime. They just hold references to objects. The Input objects all have key values, but they are the strings you set up in your layout.

tkinter treeview open state is not up to date when callback is triggered

I am currently working on an tkinter application that uses a treeview element to display data in a hierarchy. In part of this application, I have the need to update data for items. Unfortunately, getting this data is expensive (in time and processing), so I now only update items that are currently visible.
The problem I am having is that when I bind a (left click) event to an item and then query the treeview for open items to try and determine what is visible, previously opened items are returned, but the currently opened item is not (until a subsequent item is clicked).
I saw this question about querying (and setting) the open state, but it doesn't seem to cover my needs.
I have the following simplified code, which demonstrates the problem:
## import required libraries
import tkinter
import tkinter.ttk
import random
import string
## event handler for when items are left clicked
def item_clicked(event):
tree = event.widget
## we assume event.widget is the treeview
current_item = tree.item(tree.identify('item', event.x, event.y))
print('Clicked ' + str(current_item['text']))
## for each itewm in the treeview
for element in tree.get_children():
## if it has elements under it..
if len(tree.get_children(element)) > 0:
## get the element name
element_name = tree.item(element)['text']
## check if it is open
if tree.item(element)['open'] is True:
print('Parent ' + element_name + ' is open')
else:
print('Parent ' + element_name + ' is closed')
## make the window and treeview
root = tkinter.Tk()
root.title('Treeview test')
tree = tkinter.ttk.Treeview(root, selectmode = 'browse', columns = ('num', 'let'))
tree.heading('#0', text = 'Name')
tree.heading('num', text = 'Numbers')
tree.heading('let', text = 'Letters')
tree.bind('<Button-1>', item_clicked)
tree.pack()
## populate the treeview with psuedo-random data
parents = ['']
for i in range(100):
id = str(i)
## higher probability that this won't be under anything
if random.randrange(50) > 40:
parent = random.choice(parents)
else:
parent = ''
## make up some data
tree.insert(parent, 'end', iid = id, text = 'Item ' + id)
tree.set(id, 'num', ''.join(random.choices(string.digits, k=10)))
tree.set(id, 'let', ''.join(random.choices(string.ascii_uppercase, k=10)))
parents.append(id)
## main event loop (blocking)
root.mainloop()
This code will generate a treeview with 100 items in a random heirachy. Clicking on any item will print a list of items that have children and are currently expanded (open). The issue is, as with my main code, not reporting the latest item opened (i.e. it is one step behind).
Adding update() to the treeview before querying its state (at the start of the item_clicked function) does not resolve the issue.
How can I accurately get the open state of the items in the event handler of the code above?
Is it an issue that the event is being fired before the item actually expands (opens), and if so, why is an update() not fixing this?
Does an alternative function exist for treeview widgets to return a list of currently visible items?
Running on Windows 10 with Python 3.7.3 (Tk version 8.6.9).
Changing the event binding from <Button-1> to <ButtonRelease-1> in the code above resolves the issue.
It should be noted in the above code that the open/close state will only be printed for items with children that have no parent (i.e. it won't descend in to the tree). To resolve this, you simply need to call tree.get_children(item_id) on each item with children recursively.

Button Non-Event-Driven Model

First, I have read the docs. I understand that the information is stored in Key= x. My issue is when I call a function from another file it does not recognize x.
I have read the docs, but failing to understand how to use the key
I tried putting x into a variable and passing it to the function.
File 1
def add_details():
today1 = date.today()
today2 = today1.strftime("%Y/%m/%d")
create = str(today2)
name = str(_name_)
reason = str(_reason_)
startDate = str(_startDate_)
endDate = str(_endDate_)
add_data(create,name,reason,startDate, endDate)
def add_data(create,name,reason,startDate, endDate):
engine.execute('INSERT INTO schedule(Created_On, Fullname, reason, Start_Date, End_Date ) VALUES (?,?,?,?,?)',(create,name,reason,startDate,endDate))
File 2
while True:
event, values = window.Read()
print(event, values)
if event in (None, 'Exit'):
break
if event == '_subdate_': #subdate is the button Submit
sf.add_details()
My expected results are that the inputs of the GUI are passed to the function, then off to a SQLite db.
Error: name 'name' not defined
(or any key variable)
This is an example that you'll find running on Trinket (https://pysimplegui.trinket.io/demo-programs#/demo-programs/design-pattern-2-persistent-window-with-updates)
It shows how keys are defined in elements and used after a read call.
import PySimpleGUI as sg
"""
DESIGN PATTERN 2 - Multi-read window. Reads and updates fields in a window
"""
# 1- the layout
layout = [[sg.Text('Your typed chars appear here:'), sg.Text(size=(15,1), key='-OUTPUT-')],
[sg.Input(key='-IN-')],
[sg.Button('Show'), sg.Button('Exit')]]
# 2 - the window
window = sg.Window('Pattern 2', layout)
# 3 - the event loop
while True:
event, values = window.read()
print(event, values)
if event in (None, 'Exit'):
break
if event == 'Show':
# Update the "output" text element to be the value of "input" element
window['-OUTPUT-'].update(values['-IN-'])
# In older code you'll find it written using FindElement or Element
# window.FindElement('-OUTPUT-').Update(values['-IN-'])
# A shortened version of this update can be written without the ".Update"
# window['-OUTPUT-'](values['-IN-'])
# 4 - the close
window.close()

Categories

Resources