Multiselect reloads the page with Streamlit - python

I am facing an issue that when I select any items from the st.multiselect, the page is reloaded, and the selected item is also reset.
I tried to add st.session_state, but it did not help much. Can anyone have experience in this case? or any comment would be highly appreciated.
Below is my code
def load_data(self, db_result):
# Load full dataset of RecordSet
df_record_set, count_record_set = self.load_record_set_data(record_set_col)
st.write('### Full RecordSet', df_record_set)
st.info('Total items: ' + str(count_record_set))
record_set_selected_indices = st.selectbox('Select rows:', df_record_set.index)
record_set_selected_rows = df_record_set.loc[record_set_selected_indices]
st.write('### Selected Rows', record_set_selected_rows)
# Load full dataset of Exporting Template
df_exp_tem, count_exp_tem = self.load_exp_tem_data(exp_tem_col)
st.write('### Full Exporting Template', df_exp_tem)
st.info('Total items: ' + str(count_exp_tem))
exp_tem_selected_indices = st.selectbox('Select rows:', df_exp_tem.index)
exp_tem_selected_rows = df_exp_tem.loc[exp_tem_selected_indices]
st.write('### Selected Rows', exp_tem_selected_rows)
# Add a 'Generate channel' button
generate_clicked = st.button('Generate channel')
if generate_clicked or st.session_state.generate_channel:
st.session_state.generate_channel = True
# Get RecordSet Id to find related ECG with channels
record_set_id=ObjectId(record_set_selected_rows[cons.HEADER_ID])
# Query to get list of ECG channels based on RecordSet Id
result = self.load_channel_list_from_record_set(ecg_col, record_set_col, record_set_id)
# Nested For Loop
ecg_data = [
ECG(id=y[cons.ECG_ID_SHORT],
file_name=y[cons.ECG_FILE_NAME],
channel=y[cons.ECG_CHANNEL])
for x in result for y in x[ecg_col.name]
]
# Get text of channels based on selected exporting template
list_channels_str = 'Channel ' + exp_tem_selected_rows[cons.HEADER_CHANNEL]
# Widgets will be genrated by the number of ECG records
# This is used for mapping channel between ECG record and Exporting template
for x in ecg_data:
st.write(list_channels_str + ' from selected exporting template')
st.multiselect('Record: ' + x.file_name, x.channel, key=str(x))
st.success('Clicked!')
map_clicked = st.button('Extract data')
if map_clicked:
st.write('test - ''Extract data'' button is clicked')
Screenshot:
Current: When 'V5' is selected, the page is reloaded and no item is selected.
Expectation: When 'V5' is selected, the page remains stable (no reload), and the item is added to the multiselect widget. Then, other items are also can be added.
Open question: Is that possible to have 2 different st.session_state to handle 2 different business processes in a function? If not, which is the best practice?
Thanks for your reading.

Related

PyQt5 update QVBoxLayout inside scrollArea

I do have a problem while updating my content on-demand.
My scenario: I want to create a GUI which stores information about some products and it should be displayed via a scroll are. This is working fine, but when I want to update the information like the quantity of my item, the GUI or the layout of my QVBoxLayout does not update the new information on the screen.
My code for the update function:
#pyqtSlot(GroupContent)
def _updateData(self, box: GroupContent) -> None:
prompt = UpdatePrompt(self._conn, box)
prompt.exec()
amount = self._top_layout.count()
for i in range(amount):
tmp = self._top_layout.itemAt(i).widget()
if tmp.id != box.id:
continue
tmp.setTitle(box.title)
tmp.lblPrice.setText(str(box.price))
tmp.lblQuantity.setText(str(box.quantity))
The param box does already get the updated information from a QDialog.
The self._top_layout variable will be created with self._top_layout = QVBoxLayout().
I already tried to call update on the Mainwindow and also on the top layout.
Also tried directly accessing the widget with self._top_layout.itemAt(i).widget().setTitle('test') for example.
If this information is necessary, here is my function to dynamic generate the groupboxes:
def _populate(self, insert: Boolean = False) -> None:
data = self._getDBData(insert)
for row in data:
group_box = GroupContent()
group_box.storage_id.connect(self._updateData)
group_box.id = row.storage_id
group_box.title = row.name
group_box.price = row.price
group_box.quantity = row.quantity
group_box.image = row.image
self._top_layout.addWidget(group_box)

hvplot not refreshing properly when dependency changes

I am trying to establish a dashboard using jupyter, panel and hvplot. This is the code snippet.
import panel.widgets as pnw
import panel as pn
pn.extension()
# some data cleansing yields a df containing the data of interest
products = df["product_name"].drop_duplicates()
def histogram_of_variant_revenue(selected_product=None):
selection = df[df["product_name"] == selected_product]
selection["revenue"] = selection["revenue"].astype(float)
print(selection.shape)
return selection[["name", "revenue"]].drop_duplicates().set_index("name").hvplot.bar()
def histogram_of_variant_customers(selected_product=None):
selection = df[df["product_name"] == selected_product]
selection["customers"] = selection["customers"].astype(float)
print(selection.shape)
return selection[["name", "customers"]].drop_duplicates().set_index("name").hvplot.bar()
selected_product = pnw.Select(name='Product', options=sorted(list(products)))
customers = pn.bind(histogram_of_variant_customers, selected_product)
revenue = pn.bind(histogram_of_variant_revenue, selected_product)
combined_panel = pn.Column(selected_product, customers, revenue)
combined_panel
At the default suggestion.
After the next use of the drop-down selection. Notice that instead of getting a new chart - the old one seems to have moved to the right and the new one placed into the figure.
Any idea on how I can get a new histogram after selecting from the drop-down?

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.

I knew how to change a grid cell to a combobox by using wxpython GridCellChoiceEditor,but don't know how to bind for this combobox

this is a part of my code.
class AboutRelatedDialog(wx.Dialog):
def __init__(self,parent,list1,list2,list3):
wx.Dialog.__init__(self,parent,-1)
RelatedGrid = gridlib.Grid(self)
RelatedGrid.CreateGrid(sum(list2) + 1,5)
RelatedGrid.SetColLabelSize(1)
RelatedGrid.SetRowLabelSize(1)
RelatedGrid.SetCellSize(0,0,1,2)
RelatedGrid.SetCellAlignment(0,0,wx.ALIGN_CENTRE,wx.ALIGN_CENTRE)
RelatedGrid.SetCellValue(0,0,'label')
RelatedGrid.SetCellAlignment(0,2,wx.ALIGN_CENTRE,wx.ALIGN_CENTRE)
RelatedGrid.SetCellValue(0,2,'datasource')
RelatedGrid.SetCellAlignment(0,3,wx.ALIGN_CENTRE,wx.ALIGN_CENTRE)
RelatedGrid.SetCellValue(0,3,'data')
RelatedGrid.SetCellAlignment(0,4,wx.ALIGN_CENTRE,wx.ALIGN_CENTRE)
RelatedGrid.SetCellValue(0,4,'comment')
templist1 = ms.ExecQuery('SELECT RepGroup FROM RepGroup')
templist2 = []
for i in templist1:
j = i[0]
templist2.append(j)
for index in range(len(list3)):
RelatedGrid.SetCellAlignment(index + 1,1,wx.ALIGN_CENTRE,wx.ALIGN_CENTRE)
RelatedGrid.SetCellValue(index + 1,1,list3[index])
for i in range(sum(list2) + 1):
dbsource = gridlib.GridCellChoiceEditor(templist2)
RelatedGrid.SetCellEditor(i,2,dbsource)
#RelatedGrid.Bind(wx.EVT_CHOICE,self.EvtChoice,dbsource)
def EvtChoice(self,event):
print 1
and my code doesn't work,because i don't know how to bind a event for these combobox.
when i choose a datasource,i want to create another combobox to show the data that get from the RepGroup table in another cell.
so i must know how to get the event when i choose a datasoure.
You can't (easily) bind to the events generated by wxGrid editor controls, but you can handle EVT_GRID_CELL_CHANGED events generated by the grid itself whenever a value of one of its cells changes.

How to select a row in a continuously updating table?

guys.I'm working on a GUI for controlling some robots.
There is a table which shows the current states data about that robot.And the table is updated periodically from the data in the test server.When the updating was not connected it was easy to select a row in the table and save the selected row's data.However,when the server is connected, it becomes impossible to select a row(when u click on the row,the selected row will be highlighted only in a flash and gone).I try to use some print method to see what has happened inside, it seems that the slots will not be implemented when the updating is running.And the updating will also deselect the row frequently.I'm considering block the updating when I manually select a row,then automatically select the same row with a piece of logic and the selectRow() method.Do u have any suggestion for how to block the updating or another way to deal with this problem?
1.updating method, which work as a slot in a Gui Controller class to manage input
def tbRobotChanged(self, currentCell):
# get the selected cell's index from currentCell,Implement the Slot method
tablemodel= currentCell.model()
#get the model of the currentcell
firstColumn = tablemodel.index(currentCell.row(),0)
#change it to the first column in the row with the robot name
self.statusBar().showMessage("Selected Robot is " +
firstColumn.data().toString())
self.selected_robot = int(firstColumn.data().toString())
# show the selected robot name in the statusbar
2.table definition ,this is part of a Gui class which define the main window:
def initRobotsFrame(self, rf):
hbox = QtGui.QHBoxLayout()
self.robot_table = QtGui.QTableView()
self.table_header = ['Robot', 'PosX', 'PosY', 'Heading']
tm = RobotTableModel([[0, 0, 0, 2],[1, 1,2,4]],
self.table_header)
self.robot_table.setModel(tm)
vh = self.robot_table.verticalHeader()
vh.setVisible(False)
self.robot_table.resizeColumnsToContents()
self.robot_table.setSizePolicy(QtGui.QSizePolicy(
QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Minimum))
hbox.addWidget(self.robot_table)
self.selected_robot = 0
block_true=GUIController.robot_data.blockSignals(True)
GUIController.robot_data.blockSignals(block_true)
# select table by row instead of by cell:
self.robot_table.setSelectionBehavior(
QtGui.QAbstractItemView.SelectRows)
# set the signal and slot using selectionModel:
self.robot_table.selectionModel().currentRowChanged.connect(
self.tbRobotChanged)
self.robot_table.selectionModel().currentRowChanged.connect(
self.updateActiveRobot)
# implement a statusbar to test the table selection functionality:
self.statusBar().showMessage("Ready")
rf.setLayout(hbox)
3.slot to get the selected row's data:
def tbRobotChanged(self, currentCell):
# get the selected cell's index from currentCell,Implement the Slot method
tablemodel= currentCell.model()
#get the model of the currentcell
firstColumn = tablemodel.index(currentCell.row(),0)
#change it to the first column in the row with the robot name
self.statusBar().showMessage("Selected Robot is " +
firstColumn.data().toString())
self.selected_robot = int(firstColumn.data().toString())
# show the selected robot name in the statusbar

Categories

Resources