I got a table in PySimpleGui (sg.Table) and I want to add the option to hide/delete a column of that table. I found a way to do this with the data of the table, but it seems you can't edit the headings of a table on the fly. At least the .update() method doesn't provide one.
My first thought: Creating a new window with new data, without that column. But that seems like a laborious way of doing things...
Is there any clever way to do it?
You can configure ttk.Treeview (sg.Table) with option displaycolumns to select which columns are actually displayed and determines the order of their presentation. Add one more 'Row' before your displaycolumns list if you specified display_row_numbers=True.
from copy import deepcopy
import PySimpleGUI as sg
sg.theme("DarkBlue3")
newlist = [
[f"Cell ({row:0>2d}, {col:0>2d})" for col in range(8)]
for row in range(10)
]
COL_HEADINGS = ["Date", "Ref", "ID", "Owner", "Customer", "Price", "Size", "Path"]
layout = [
[sg.Table(
values=newlist,
headings=COL_HEADINGS,
max_col_width=25,
num_rows=10,
alternating_row_color='green',
display_row_numbers=True,
key='-TABLE-',
enable_click_events=True,
justification='center',
)],
[sg.Button('Hide Customer')],
]
window = sg.Window('Table', layout, finalize=True)
table = window['-TABLE-']
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == 'Hide Customer':
window[event].update(disabled=True)
displaycolumns = deepcopy(COL_HEADINGS)
displaycolumns.remove('Customer')
table.ColumnsToDisplay = displaycolumns
table.Widget.configure(displaycolumns=['Row']+displaycolumns)
window.close()
Related
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.
I want to make a widget window, but the Bottom and Text part aren't working properly and it keps getting sintax errors at the same part:
import PySimpleGUI as sg
layout = [
[sg.Text("Hello from PySimpleGUI")],
[sg.Button("Close")]
window = sg.Window("Demo, layout")
]
while true:
event, values = window.read()
if event == "Close" or event == sg.WIN_CLOSED:
break
window.close()
it says I forgot a comma, but the references I checked for this code didn't use one, and I tried changing the elements or just putting the comma, but it didn't work either.
ERROR message:
line 5
[sg.Buttom("OK")]
^^^^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
This:
layout = [
[sg.Text("Hello from PySimpleGUI")],
[sg.Button("Close")]
window = sg.Window("Demo, layout")
]
isn't a valid list, because you can't include an assignment like that, and there should be a comma after the second element. Also, the call to sg.Window() doesn't look right, because presumably you meant to pass layout as the second argument to define the layout of a window named "Demo".
Try doing this:
layout = [
[sg.Text("Hello from PySimpleGUI")],
[sg.Button("Close")]
]
window = sg.Window("Demo", layout)
If you need to access an object in a list, you either need to create it before hand, like this:
window = sg.Window("Demo")
layout = [
[sg.Text("Hello from PySimpleGUI")],
[sg.Button("Close")],
[window]
]
Or do something ugly that relies on knowing the internal structure, like this:
layout = [
[sg.Text("Hello from PySimpleGUI")],
[sg.Button("Close")],
[sg.Window("Demo")]
]
window = layout[2][0]
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.
While working on my project I came across a problem regarding tabs and tabgroup in PySimpleGUI.
Is there a function that returns currently selected tab?
Having many tabs I would like being able to select each of them (but not at the same time) and return the key of the currently selected one/active one.
I've tried .Get() and .Select() but seems like these don't do it or maybe I'm doing something wrong? Please keep in mind I'm a total beginner.
I also made sure to enable events in every tab.
I did something like this: curr_sel_tab= tab_group.find_key_from_tab_name(tab_group.Get()) but it returns the name of the tab instead of the key.
Basically my app is connected to a database. What I'm trying to do is to select a row in a tab(1,2,3 or 4) and delete it. But in order to do it I need a key of the tab(I guess).
Below you can find a fragment of code I stuck upon as well as a screenshot of my app.
import PySimpleGUI as sg
import baza # external file with my databse
sg.theme("GreenTan")
left_col = [sg.Button("Create")],[sg.Button("Read")],[sg.Button("Update")],[sg.Button("Delete")]
data = baza.get_db_obiad()
print(data)
headings2 = ['Id', 'Name', '1', '2', '3']
layout_1 = [[sg.Table(values=data[0:][:], headings=headings2, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_1-',
row_height=35)]]
data1 = baza.get_db_podkladka()
headings3 = ['Id','Name']
layout_2 = [[sg.Table(values=data1[0:][:], headings=headings3, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_2-',
row_height=35)]]
data2 = baza.get_db_mieso()
headings4 = ['Id','Name']
layout_3 = [[sg.Table(values=data2[0:][:], headings=headings4, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_3-',
row_height=35)]]
data3 = baza.get_db_dodatki()
headings5 = ['Id','Name']
layout_4 = [[sg.Table(values=data3[0:][:], headings=headings5, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_4-',
row_height=35)]]
tab_group = sg.TabGroup([[sg.Tab("Tab 1", layout_1),
sg.Tab("Tab 2", layout_2),
sg.Tab("Tab 3", layout_3),
sg.Tab("Tab 4", layout_4)]],
enable_events=True)
right_col = [[tab_group]]
layout = [[sg.Column(left_col, justification="c"), sg.Column(right_col)]]
window = sg.Window("", layout).Finalize()
window.Maximize()
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == "Exit":
break
elif event == "Create":
pass
elif event == "Read":
pass
elif event == "Update":
pass
elif event == "Delete":
if sg.popup_yes_no("Are you sure you want to delete this record?"):
curr_sel_tab= tab_group.find_key_from_tab_name(tab_group.Get())
print(curr_sel_tab)
else:
break
window.close()
Screenshot from my app
I had a similar issue! I was able to figure out that the values object contains the key of the tab that has been clicked on. You would just need to figure out what index its at. Before you do that, it would be a good idea to give each tab a key so that you know what tab was clicked on. Made some edits to your code below. Notice how I added the keys to teach tab:
tab_group = sg.TabGroup([[sg.Tab("Tab 1", layout_1, key= "tab1"),
sg.Tab("Tab 2", layout_2, key="tab2"),
sg.Tab("Tab 3", layout_3, key="tab3"),
sg.Tab("Tab 4", layout_4, key="tab4")]],
enable_events=True)
If you print out the values object and "Tab 1" is clicked, then you will see its key, "tab1" in this case, inside the values object. Same applies for the other tabs if they are clicked.
Hope this helps!
All,
I am trying to implement a dash datatable, where I select rows by a direct click on it (no radio buttons). Currently, I am doing this with the active_cell and it works well:
No matter in which cell of a row a user clicks, a graph is updated with the data in that row. If he clicks another cell in the same row, the data is unselected (via a dcc.storage)
Here comes my problem: If the user clicks the same cell again, there is no active_cell event triggered. Therefore, my callback is not triggered and nothing happens.
I would like to deselect that cell the second time the user clicks it. How can I implement that?
Thanks!
So... I solved this... it is not pretty but it works - it includes a loopbreaker which I had to implement to avoid a circular dependency, but yeah - I am absolutely open for cleaner solutions.
Find below the callbacks
# takes user selected cell (active_cell) and the current state of a dcc.storage (tableclick) which stores the last saved row that was clicked
# output: updated selected_rows for the datatable, styling for the selected row and update for dcc.storage
# if no cell is selected, do nothing
# if no cell is selected, but there is a row stored as selected, highlight that row (this is a consequence from the circular dependency)
# if a cell is selected that is different from the previous cell, highlight that new row. Otherwise, deselect the row.
#app.callback(
[Output('performancedatatable', 'style_data_conditional'), Output('tableclick', 'data'),
Output('performancedatatable', 'selected_rows')],
[
Input('performancedatatable', 'active_cell'),
], [State('tableclick', 'data')]
)
def highlight_row(cell, prevrow):
if cell is None:
if prevrow is None:
return [{}], None, []
else:
return [{}], None, prevrow
elif "row" in cell:
if cell.get("row", "") == prevrow:
return [{}], None, []
else:
return ([{
"if": {"row_index": cell.get("row", "")},
"backgroundColor": "rgba(146, 192, 234, 0.5)",
}], cell.get("row", ""), [cell.get("row", "")])
# Is triggered by changing the dcc.storage "tableclick"
# resets active cell and selected cell via callback below
#app.callback([Output('loopbreaker_div', "children")], [Input('tableclick', 'data')])
def reset_active_cell(input):
return [html.Div(id='loopbreaker', children=True)]
#loopbreaker to avoid circular dependency
#app.callback([Output('performancedatatable', "active_cell"), Output('performancedatatable', 'selected_cells')], [Input('loopbreaker', 'children')])
def reset_active_cell(input):
time.sleep(1)
return (None, [])
Shoutout to http://yaaics.blogspot.com/2019/03/circular-references-in-plotlydash.html for helping resolving the circular dependency
A simpler solution (but it has other drawbacks) is to select a hidden cell. This makes it appear to the user that nothing is selected.
In the example below a cell is processed and de-selected by a callback. Clearly this callback could also be for a "deselect all" button or whatever else you need.
Add column called "cant_see":
df = pd.read_csv("my_data.csv")
df.insert(0, "cant_see", ["" for i in df.iloc[:, 0] ])
Make it hidden when you create the DataTable using style_data_conditional and style_header_conditional:
dash_table.DataTable(
id="table",
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict("records"),
is_focused=True,
style_data_conditional=[
{'if': {'column_id': 'cant_see',}, 'display': 'None',}
],
style_header_conditional=[
{'if': {'column_id': 'cant_see',}, 'display': 'None',}
],
)
and then a callback can set the table's "active_cell" and/or "selected_cells"
#app.callback(
Output("table", "active_cell"),
Output("table", "selected_cells"),
Input("table", "active_cell"),)
def cell_clicked(cell):
# Do something useful with cell,
# such as toggling it's style to mimic select/de-select
# Now make it look like the cell is de-selected
# by selecting a hidden cell
#
# return active cell 0,0 and selected_cells list [ 0,0 ]
return {'column':0, 'row':0}, [{'column':0, 'row':0}]