This is my code and although I initialize the session state, I'm having a conflict with it.
import streamlit as st
if "user_inputs" not in st.session_state:
st.session_state["user_inputs"] = True
# Loop over the 8 questions
for i in range(8):
# Get the question
question = f'Question {i+1}'
# Add it to the page
st.subheader(question)
# Create 3 checkbox options
checkbox1 = st.checkbox('Option 1')
checkbox2 = st.checkbox('Option 2')
checkbox3 = st.checkbox('Option 3')
# Save the checkbox inputs in the session state object
st.session_state.user_inputs[f'{question}_checkbox_1'] = checkbox1
st.session_state.user_inputs[f'{question}_checkbox_2'] = checkbox2
st.session_state.user_inputs[f'{question}_checkbox_3'] = checkbox3
# Create an integer input
integer_input = st.number_input('Integer Input')
# Save the integer input in the session state object
st.session_state.user_inputs[f'{question}_integer_input'] = integer_input
# Create a slider
slider = st.slider('Slider', 0, 100)
# Save the slider value in the session state object
st.session_state.user_inputs['slider'] = slider
# Add a submit button
if st.button('Submit'):
st.success('Form submitted!')
I tried different ways to initialize but they didn't work. When I try something like st.session_state.user_inputs = "test" or st.session_state["user_inputs"] = "test", again I have same error:
The error is:
st.session_state has no attribute "user_inputs". Did you forget to
initialize it? More info:
https://docs.streamlit.io/library/advanced-features/session-state#initialization
I tried to create a kind of form but I received a repetitive error.
The error I get for your main block of code is TypeError: 'bool' object does not support item assignment.
This makes sense, because st.session_state["user_inputs"] = True means you've set up a boolean, not a dictionary, so when you do:
st.session_state.user_inputs[f'{question}_checkbox_1'] = checkbox1
you're basically doing:
True[f'{question}_checkbox_1'] = checkbox1
giving the error. Instead, use:
if "user_inputs" not in st.session_state:
st.session_state["user_inputs"] = {}
Next, you have duplicate keyed inputs. Try adding unique keys to these:
checkbox1 = st.checkbox('Option 1', key=f'{i}-1')
checkbox2 = st.checkbox('Option 2', key=f'{i}-2')
checkbox3 = st.checkbox('Option 3', key=f'{i}-3')
# ...
integer_input = st.number_input('Integer Input', key=i)
Note also that keyed user inputs are automatically added to state, which is a bit surprising, but that's Streamlit for you. If you do prefer to explicitly set them, I'd use actual nested structures like 2d lists or dicts rather than keys with concatenated identifiers.
Finally, you may want to use st.form to group all of your inputs into a collection, avoiding triggering rerenders.
Related
I am trying to create a drop down menu in tkinter that allows the user to select a machine, or row # from the excel sheet, and then all the data from that entire row is displayed in tkinter through the display_selected function. I keep getting this error.
This is my error:
TypeError: line_7_window.<locals>.display_selected() takes 0 positional arguments but 1 was given
This is my code:
def get_machine():
for row in sheet.iter_rows(min_row=2, max_col=1, max_row=60):
for cell in row:
if cell.value == inputmachine:
machineindex = cell
return machineindex.row
def get_machineattributes():
for col in sheet.iter_cols(min_row = (get_machine()), max_col = 15, max_row = (get_machine())):
for cell in col:
return (cell.value)
def display_selected():
data = Message(line_7, text=get_machineattributes())
data.pack()
data.place(x=650, y=30)
copy = Button(line_7, text="Copy to Clipboard", command=pyperclip.copy(line7_choice))
copy.pack()
copy.place(x=550, y=45)
return
inputmachine = StringVar(line_7)
inputmachine.set("Click to select a machine")
dropdown = OptionMenu(line_7, inputmachine, *lst, command=display_selected)
dropdown.pack()
dropdown.place(x=670, y=25)
I have tried everything and I cant figure out why this would not work.
Your callback function display_selected, specified when creating the option menu, will receive the actual option chosen from Tk, so you need that parameter when defining it, even if you do nothing with it.
In other words, use something like:
def display_selected(choice):
del choice # not used
# rest of function
As an aside, I suspect you may be better off with an option menu to select the item, and a separate button to act on that selection. That would allow you to confirm the selected item before performing any action, in case you inadvertently choose the wrong one.
def in_Vals():
in_win = Tk()
in_win.title("Check In Details")
in_win.geometry("700x700")
in_win.resizable(0,0)
# title
title = Label(in_win,text="Check In Details",font=("Harlow Solid Italic",30,"italic"),fg="black",bg="#fbb08c")
title.pack(anchor="center",pady=5)
#creating label's
_Id_ = Label(in_win,text="Id :",font=("Times New Roman",15,"italic"),fg="black",bg="#fbb08c")
_Name_ = Label(in_win,text="Name :",font=("Times New Roman",15,"italic"),fg="black",bg="#fbb08c")
_Date_ = Label(in_win,text="Date :",font=("Times New Roman",15,"italic"),fg="black",bg="#fbb08c")
_Time_ = Label(in_win,text="Time :",font=("Times New Roman",15,"italic"),fg="black",bg="#fbb08c")
_Number_ = Label(in_win,text="Number :",font=("Times New Roman",15,"italic"),fg="black",bg="#fbb08c")
_Id_.pack(anchor='w',padx=10,pady=20)
_Name_.pack(anchor='w',padx=10,pady=20)
_Date_.pack(anchor='w',padx=10,pady=20)
_Time_.pack(anchor='w',padx=10,pady=20)
_Number_.pack(anchor='w',padx=10,pady=20)
# creating submit function
def submit():
print(f"{in_val_1}\n{in_val_2}\n{in_val_3}\n{in_val_4}\n{in_val_5}")
# creating entries
Id = Entry(in_win,width=25,font=("Courier",15,'bold'))
Name = Entry(in_win,width=25,font=("Courier",15,'bold'))
Date = Entry(in_win,width=25,font=("Courier",15,'bold'))
Time = Entry(in_win,width=25,font=("Courier",15,'bold'))
Number = Entry(in_win,width=25,font=("Courier",15,'bold'))
Id.place(x=100,y=87)
Name.place(x=100,y=157)
Date.place(x=100,y=227)
Time.place(x=100,y=293)
Number.place(x=100,y=360)
#getting values
in_val_1 = Id.get()
in_val_2 = Name.get()
in_val_3 = Date.get()
in_val_4 = Time.get()
in_val_5 = Number.get()
# creating submit button
submit = Button(in_win,text="Submit",font=("Wild Latin",15,"bold"),command=submit)
submit.place(x = 250,y=450)
in_win.config(bg="#fbb08c")
in_win.mainloop()
Here the function in_vals() is a coded to take data from the ID, Name, Date, Time, Number Entries and assign the values of The entries to the variables in_val_1 to in_val_5 ,to get the values from the entry box I have used the .get() Method. but when I try to Print the Variables that I assigned to the .get() method, it prints some white Space's.
The solution for the problem same as mine is
defining the values outside the the button function does not get anything.
here I have defined out side the button function
after defining it inside the button function it gives me the desired output
I am writing a UI for a simulation program which accepts tabular data.
The basic functionality I need is for the user to be able to enter / change data in cells either by directly typing into them, or by pasting data (usually from an excel sheet). The program checks this data and either accepts or rejects it before running the simulation. I also want to let the user type in their own column headers for the table.
Tksheet is an awesome Tkinter add-on, giving an excel-like "feel" to the input frame, but its documentation leaves much to be desired. (For instance: each event generates a different event-information array--see code for two event-processing routines below--but nowhere is it specified what these parameters are. It is left for the user to discover using trial and error, or trying to read the source code--which is not documented either).
I have two specific questions:
Is there any way to not-commit, or to roll back, changes to the table? If my data-tests fail, how do I prevent potentially harmful user input from being entered into the table?
Obviously I can (and do) add a begin_*** event in which I can keep copies of the original values, and then reset the table values if the data testing at the end_*** event fails, but this is wasteful and inelegant. I have a feeling that the set_data_ref_on_destroy property has something to do with such a capability, but the documentation does not explain what this parameter is or how to use it.
How can I change a single column header at a time? The .headers property seems to work only with a full list of headers starting with column 0 (if I run self.sheet.headers([single_value], index = i) it ignores the index parameter and plugs single_value in column 0)
Again, I can set the column headers to something non-default at init and keep a running list of all headers, so that I can reset all the headers on each change, but this is wasteful and inelegant.
In the following code sample I set up a simple table, and bind three user-generated events: one for typing a value to a cell, one for pasting a block of values, and one for adding an option to the right-click menu of a column header, to allow the user to type a name to the column.
from tksheet import Sheet
import tkinter as tk
import tkinter.messagebox as msg
import tkinter.simpledialog as sd
class demo(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.grid_columnconfigure(0, weight=1) # This configures the window's escalators
self.grid_rowconfigure(0, weight=1)
self.frame = tk.Frame(self)
self.frame.grid_columnconfigure(0, weight=1)
self.frame.grid_rowconfigure(0, weight=1)
self.frame.grid(row=0, column=0, sticky="nswe")
self.sheet = Sheet(self.frame, data=[[]]) # set up empty table
self.sheet.grid(row=0, column=0, sticky="nswe")
self.sheet.enable_bindings(bindings= # enable table behavior
("single_select",
"select_all",
"column_select",
"row_select",
"drag_select",
"arrowkeys",
"column_width_resize",
"double_click_column_resize",
"row_height_resize",
"double_click_row_resize",
"right_click_popup_menu",
"rc_select", # rc = right click
"copy",
"cut",
"paste",
"delete",
"undo",
"edit_cell"
))
# Note that options that change the structure/size of the table (e.g. insert/delete col/row) are disabled
# make sure that pasting data won't change table size
self.sheet.set_options(expand_sheet_if_paste_too_big=False)
# bind specific events to my own functions
self.sheet.extra_bindings("end_edit_cell", func=self.cell_edited)
self.sheet.extra_bindings("end_paste", func=self.cells_pasted)
label = "Change column name" # Add option to the right-click menu for column headers
self.sheet.popup_menu_add_command(label, self.column_header_change, table_menu=False, index_menu=False, header_menu=True)
# Event functions
def cell_edited(self, info_tuple):
r, c, key_pressed, updated_value = info_tuple # break the info about the event to individual variables
if check_input(updated_value):
pass # go do stuff with the updated table
else:
msg.showwarning("Input Error", "'" + updated_value + "' is not a legal value")
pass # what do I do here? How do I make tksheet *not* insert the change to the table?
def cells_pasted(self, info_tuple):
key_pressed, rc_tuple, updated_array = info_tuple # break the info about the event to individual variables
r, c = rc_tuple # row & column where paste begins
if check_input(updated_array):
pass # go do stuff with the updated table
else:
msg.showwarning("Input Error", "pasted array contains illegal values")
pass # what do I do here? How do I make tksheet *not* insert the change to the table?
def column_header_change(self):
r, c = self.sheet.get_currently_selected()
col_name = sd.askstring("User Input", "Enter column name:")
if col_name is not None and col_name != "": # if user cancelled (or didn't enter anything), do nothing
self.sheet.headers([col_name], index=c) # This does not work - it always changes the 1st col
self.sheet.redraw()
# from here down is test code
def check_input(value): # instead of actual data testing we let the tester choose a pass/fail response
return msg.askyesno("Instead of input checking","Did input pass entry checks?")
test = demo()
lst = ["hello", "world"]
test.sheet.insert_column(values=lst)
lst = [0, "hello", "yourself"]
test.sheet.insert_column(values=lst)
test.mainloop()
I realize that the original post is now 5 months old, and I'm a relative n00b, but I hope this helps.
Given a tksheet instance 'sheet' that has already been populated with headers ["A"."B"."C"], the following works to change the header "B" to "NEW":
sheet.headers()[1]="NEW"
Hope this helps.
I want to drag and drop a list item to another list. Both lists got two columns. I got it working with drag and dropping the value from the first column to the other list. But drag and drop bot values from column 0 and 1 to the other list wont work.
How do i use DropSource.SetData() with a list?
Here is the part of my Code:
def OnDragInit(self, event):
#text = self.lst1.GetItemText(event.GetIndex(),0)
#tobj = wx.TextDataObject(text) #Doesnt work with a list
# With the above two lines everything is working fine!
# Error here
text = []
text.append(self.lst1.GetItemText(event.GetIndex(),0))
text.append(self.lst1.GetItemText(event.GetIndex(),1))
src = wx.DropSource(self.lst1)
src.SetData(text)
src.DoDragDrop(True)
self.lst1.DeleteItem(event.GetIndex())
Here is the error message:
TypeError: DropSource.SetData(): argument 1 has unexpected type 'list'
I suspect that you are using a wx.TextDropTarget with a wx.TextDataObject and what you are passing is clearly a list.
You will need to create custom data object, then serialise the list on drag and de-serialise it on drop.
To serialise/de-serialise the list you can use pickle or marshal and I'm led to believe that you could use json as well but I didn't really look into that.
The coding for a list drag & drop can get quite complicated, so I've put this code together for you to visualise how you might implement it.
The code is commented, so hopefully you will be able to see what is what.
It is slightly more complicated than it has to be, as I have allowed for dragging and dropping from/to lists with an unequal number of columns.
Also note, that you can drag and drop onto the same list.
import wx
import pickle
#import marshal
class MyTarget(wx.PyDropTarget):
def __init__(self, object):
wx.DropTarget.__init__(self)
self.object = object
# specify the type of data to accept
self.data = wx.CustomDataObject("ListCtrlItems")
self.SetDataObject(self.data)
# Called when OnDrop returns True.
def OnData(self, x, y, opt):
# Find insertion point in the target.
index, flags = self.object.HitTest((x, y))
if self.GetData():
# unpickle data
listdata = self.data.GetData()
dropped_list = pickle.loads(listdata)
#dropped_list = marshal.loads(listdata)
if index == -1: # if not inserting, set index to the end of the listctrl
index = self.object.GetItemCount()
#Insert at drop point
for row in dropped_list:
self.object.InsertItem(index, row[0]) #Insert item
cols = self.object.GetColumnCount()
for pos in range(1,cols):
try:
self.object.SetItem(index, pos, row[pos]) #Add extra columns data
except Exception as e: # run out of columns in target
pass
index +=1
return True
class Mywin(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self, parent, wx.ID_ANY, title,size= (600,-1))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.HORIZONTAL)
self.listCtrl1 = wx.ListCtrl(panel, -1, style = wx.LC_REPORT|wx.LC_HRULES)
self.listCtrl1.InsertColumn(0, "Item0")
self.listCtrl1.SetColumnWidth(0,100)
self.listCtrl1.InsertColumn(1, "Item1")
self.listCtrl1.SetColumnWidth(1,100)
self.listCtrl2 = wx.ListCtrl(panel, -1, style = wx.LC_REPORT|wx.LC_VRULES|wx.LC_HRULES)
self.listCtrl2.InsertColumn(0, "Item0")
self.listCtrl2.SetColumnWidth(0,100)
self.listCtrl2.InsertColumn(1, "Item1")
self.listCtrl2.SetColumnWidth(0,100)
self.listCtrl2.InsertColumn(2, "Item2")
self.delete = wx.CheckBox(panel, wx.ID_ANY, "Delete on move")
self.delete.SetToolTip("Delete original item when dragged & dropped")
self.delete.SetValue(True)
#load sample data
data = [["abc",1],["def",2],["ghi",3]]
for i in data:
self.listCtrl1.Append((i))
data = [["ABC",1,"first"],["DEF",2,"second"],["GHI",3,"third"]]
for i in data:
self.listCtrl2.Append((i))
#Target Left
tl = MyTarget(self.listCtrl1)
self.listCtrl1.SetDropTarget(tl)
#Target Right
tr = MyTarget(self.listCtrl2)
self.listCtrl2.SetDropTarget(tr)
self.listCtrl1.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDrag)
self.listCtrl2.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDrag)
box.Add(self.listCtrl1, 0, wx.EXPAND)
box.Add(self.listCtrl2, 0, wx.EXPAND)
box.Add(self.delete, 0, wx.ALIGN_TOP)
panel.SetSizer(box)
panel.Fit()
self.Centre()
self.Show(True)
def OnDrag(self, event):
#create a data object for drag-and-drop
object = event.GetEventObject() # listCtrl1 or listCtrl2
list_data = []
idx = -1
while True: # find all the selected items and put them in a list
idx = object.GetNextSelected(idx)
if idx == -1:
break
item_data = []
for item in range(object.GetColumnCount()): # get data from all columns
item_data.append(object.GetItem(idx, item).GetText())
list_data.append(item_data)
# Pickle the items list.
pickle_data = pickle.dumps(list_data)
#pickle_data = marshal.dumps(list_data)
# create custom data object
cdataobj = wx.CustomDataObject("ListCtrlItems")
cdataobj.SetData(pickle_data)
# Now make a data object for the item list.
data = wx.DataObjectComposite()
data.Add(cdataobj)
# Create drop source and begin drag-and-drop.
dropSource = wx.DropSource(object)
dropSource.SetData(data)
result = dropSource.DoDragDrop(True)
# delete dropped items from source list
if self.delete.GetValue(): # Is delete checkbox ticked
if result == wx.DragCopy: # Was the drag and drop successful
while True:
#For this small sample always start at the beginning (-1)
idx = object.GetNextSelected(-1)
if idx == -1: #No more selected items
break
object.DeleteItem(idx)
demo = wx.App()
Mywin(None,'Drag & Drop ListCtrl Demo')
demo.MainLoop()
In answer to your comment about the result coming back from the DoDragDrop:
The DragResult enumeration provides the following values:
Description Value
DragError Error prevented the D&D operation from completing.
DragNone Drag target didn’t accept the data.
DragCopy The data was successfully copied.
DragMove The data was successfully moved (MSW only).
DragLink Operation is a drag-link.
DragCancel The operation was cancelled by user (not an error).
DoDragDrop(self, flags=Drag_CopyOnly) Starts the drag-and-drop
operation which will terminate when the user releases the mouse.
Call this in response to a mouse button press, for example.
Parameters: flags (int) – If wx.Drag_AllowMove is included in the
flags, data may be moved and not only copied as is the case for the
default wx.Drag_CopyOnly . If wx.Drag_DefaultMove is specified (which
includes the previous flag), moving is not only possible but becomes
the default operation. Return type: wx.DragResult Returns: The
operation requested by the user, may be wx.DragCopy , wx.DragMove ,
wx.DragLink , wx.DragCancel or wx.DragNone if an error occurred.
I've got some code in a class that extends gtk.TreeView, and this is the init method. I want to create a tree view that has 3 columns. A toggle button, a label, and a drop down box that the user can type stuff into. The code below works, except that the toggle button doesn't react to mouse clicks and the label and the ComboEntry aren't drawn. (So I guess you can say it doesn't work). I can add rows just fine however.
#make storage enable/disable label user entry
self.tv_store = gtk.TreeStore(gtk.ToggleButton, str, gtk.ComboBoxEntry)
#make widget
gtk.TreeView.__init__(self, self.tv_store)
#make renderers
self.buttonRenderer = gtk.CellRendererToggle()
self.labelRenderer = gtk.CellRendererText()
self.entryRenderer = gtk.CellRendererCombo()
#make columns
self.columnButton = gtk.TreeViewColumn('Enabled')
self.columnButton.pack_start(self.buttonRenderer, False)
self.columnLabel = gtk.TreeViewColumn('Label')
self.columnLabel.pack_start(self.labelRenderer, False)
self.columnEntry = gtk.TreeViewColumn('Data')
self.columnEntry.pack_start(self.entryRenderer, True)
self.append_column(self.columnButton)
self.append_column(self.columnLabel)
self.append_column(self.columnEntry)
self.tmpButton = gtk.ToggleButton('example')
self.tmpCombo = gtk.ComboBoxEntry(None)
self.tv_store.insert(None, 0, [self.tmpButton, 'example label', self.tmpCombo])
First of all, you need to create a model with bool, str and str columns, not the way you are doing now. Second, you need to bind properties of renderers from appropriate model columns, e.g. as in
self.columnButton = \
gtk.TreeViewColumn ('Enabled', self.buttonRenderer,
active = 0) # 0 is the tree store column index
Then you need to set editable property on the renderer to True. And finally, you need to handle signals (changed or editing-done, depending on renderer type) yourself and update the store accordingly.
It may be easier to use some helpers, e.g.
Py-gtktree — there's even an example for editing a tree there.
Just connnect the toggled signal in the gtk.CellRendererToggle, when you click on it, it will emit that signal, then in your callback change the value in the model.
ej.
def toggle(self, cellrenderer, path):
Self.model[path][column] = not self.model[path][column]
self.model is the model asociated to the treeview,