Modify Checkboxes based on changes in Streamlit - python

I wanted to create a checkbox which checks all other checkboxes if checked and if the checkbox is unchecked that all other checkboxes also get unchecked.
For example:
Clicking on “all” should be also checking “option1, option2, etc.”. Same if I uncheck.
I tried to use session_states but could not come up with a solution.
Is there a possibility to modify checkboxes?

You can use this way.
import streamlit as st
isall = st.checkbox(label="All")
st.checkbox(label="option1", value=isall)
st.checkbox(label="option2", value=isall)

Related

Removing the selected items from streamlet's multiselect

I am trying to delete objects from a list by choosing them via streamlits multiselect widget, having the list entries as entries of the widget. Thus, the list also decides what options are in the multiselect box.
However, after the app reruns—once I deleted some options—I get the error: streamlit.errors.StreamlitAPIException: Every Multiselect default value must exist in options
Here is some minimal code example.
import streamline as st
if st.button("Refill") or "options" not in st.session_state:
st.session_state.options=["a","b","c"]
def submit():
for item in st.session_state.selected:
st.session_state.options.remove(item)
form=st.form("My form")
form.multiselect("Select", st.session_state.options, key="selected")
form.form_submit_button("Submit", on_click=submit)
I tried to add the line
st.session_state.selected=[]
to the submit function so that the multiselect-box is cleared and does not reference deleted items, but it did not solve the issue.
Thanks for any help in advance! :)
Add the following to the top of your code:
if "selected" in st.session_state:
del st.session_state.selected
Explanation:
The streamlit multi-select widget maintains the last selection in its internal state (st.session_state.selected in your case), so if you delete an item from your st.session_state.options list, it will error out, as it can't find the current selection in the list you passed to it.
To fix this, simply delete the session_state.selected prior to running the rest of the code; this is done with the suggested if-Statement.
You might also want to add the kwarg clear_on_submit=True on your st.form definition, as that clears the input of the form as well.

update radio button in PySimpleGUI

I'm using PySimpleGUI in which I want to update a radio button. According to the documentation the radio button has an update method. But somehow it doesn't work properly.
I wrote the following code which should update the value of the radio button from Test to NewTest. The result is still Test.
Code used below:
import PySimpleGUI as sg
layout1 = [[sg.Radio('Test', "RADIO1", key='_RADIO1_', default=True, font=50)],
[sg.Button('Ok', font=50), sg.Button('Stop', font=50)]]
window = sg.Window('Read').Layout(layout1).Finalize()
while True:
window.Element('_RADIO1_').Update('NewTest')
button, values = window.Read()
exit()
Sounds like you're trying to change the text that's next to an specific radio button.
The problem is that each of the PySimpleGUI Elements has a slightly different Update method. Simply put, the things you can change in a Radio Element are:
Update(self, value=None, disabled=None, visible=None)
While the documentation on the Radio Button element's Update is brief in the documentation, it is described there https://pysimplegui.readthedocs.io/#radio-button-element
Update(value=None, disabled=None, visible=None)
value - bool - if
True change to selected
disabled - if True disables the element
There are 3 things you can currently change in a Radio button, the "state" (true/false), disabled and visibility.
I would suggest logging this as a feature request Issue on the GitHub site (http://www.PySimpleGUI.com). These requests are often implemented quite quickly.

PywinAuto and the disappearing control Identifier

I'm using pywinauto (the latest from the new github) to automate logging in to another program. The "Sign in" window has a bunch of buttons, and two fields, one for user name and one for password.
My problem is that the user name and password 'edit' control identifiers have the same Access name: ['1', '0', 'Edit']. There is no "Edit2".
When I use
sign_in.print_control_identifiers()
It still only shows the one edit property. How do I access this other edit control?
update with pictures with the demo company file:
Here is the LOGIN window: http://imgur.com/VwS9w0b
Here is the mouse hovering over password: http://imgur.com/6HWQVlZ
Password field clicked, its called edit1 as well! http://imgur.com/GUnTVrK
Swapy output : http://imgur.com/LJB99y1
A solution I found was to simulate a "tab" key
sign_on.TypeKeys("{TAB}")
But this is not a great solution because if another window were to take focus at the time of the TAB then the script would sent the tab to that window.
I'm not sure which version of Pywinauto you are using. There is a revived one on GitHub (https://github.com/pywinauto/pywinauto). You can access controls as elements of a dictionary:
sign_in['0']
sign_in['1']
Highlighting a GUI element can also help to understand what element you refer to:
sign_in['0'].DrawOutline() # green by default
sign_in['1'].DrawOutline('red') # acceptable keywords: 'red','blue','green'
sign_in['Edit'].DrawOutline(0xff0000) # blue
Update the answer with an example of walking through all control's children and highlight them. This way you can see if you have access to the "password" field.
import time
def drawContours(ctl):
for c in ctl.Children():
drawContours(c)
time.sleep(1)
c.DrawOutline()
ctl.DrawOutline()
drawCountours(sign_in)
Edit0 and Edit1 refers to the same first edit box. It's expected behavior (by design). Edit2 refers to the second edit box, Edit3 to the third one etc. If you get print_control_identifiers() output, you usually see something like that ("Find" dialog in Notepad, for example):
Edit - '' (L152, T160, R323, B180)
'Edit' 'Edit0' 'Edit1' 'Fi&nd what:Edit' ()
Edit - '' (L152, T188, R323, B208)
'Edit2' 'Re&place with:Edit' ()
So possible names for best_match search algorithm are listed for each control. These names are trying to be unique (not overlapping with other controls), but one control has several best names. It's normal situation. sign_in['Edit2'] is probably what you need.
If you disagree with such approach, you may raise design discussion here: https://github.com/pywinauto/pywinauto/issues

PyQt - How to connect multiple signals to the same widget

[ ]All1 [ ]All2
[ ]checkbox1A [ ]checkbox1B
[ ]checkbox2A [ ]checkbox2B
Based on the chart above, a few things need to happen:
The All checkboxes only affect the on/off of the column it resides in, and checks on/off all the checkboxes in that column.
All checkboxes work in pairs, so if checkbox1A is on/off, checkbox1B needs to be on/off
If an All checkbox is checked on, and then the user proceeds to check off one or more checkbox in the column, the All checkbox should be unchecked, but all the checkboxes that are already checked should remain checked.
So really this is more like a chain reaction setup. If checkbox All1 is on, then chieckbox1A and 2A will be on, and because they are on, checkbox1B and 2B are also on, but checkbox All2 remains off. I tried hooking up the signals based on this logic, but only the paired logic works 100%. The All checkbox logic only works 50% of the time, and not accurately, and there's no way for me to turn off the All checkbox without turning all already checked checkboxes off.
Really really need help ... T-T
Sample code:
cbPairKeys = cbPairs.keys()
for key in cbPairKeys:
cbOne = cbPairs[key][0][0]
cbTwo = cbPairs[key][1][0]
cbOne.stateChanged.connect(self.syncCB)
cbTwo.stateChanged.connect(self.syncCB)
def syncCB(self):
pairKeys = cbPairs.keys()
for keys in pairKeys:
cbOne = cbPairs[keys][0][0]
cbOneAllCB = cbPairs[keys][0][4]
cbTwo = cbPairs[keys][1][0]
cbTwoAllCB = cbPairs[keys][1][4]
if self.sender() == cbOne:
if cbOne.isChecked() or cbTwoAllCB.isChecked():
cbTwo.setChecked(True)
else:
cbTwo.setChecked(False)
else:
if cbTwo.isChecked() or cbOneAllCB.isChecked():
cbOne.setChecked(True)
else:
cbOne.setChecked(False)
EDIT
Thanks to user Avaris's help and patience, I was able to reduce the code down to something much cleaner and works 100% of the time on the 1st and 2nd desired behavior:
#Connect checkbox pairs
cbPairKeys = cbPairs.keys()
for key in cbPairKeys:
cbOne = cbPairs[key][0][0]
cbTwo = cbPairs[key][1][0]
cbOne.toggled.connect(cbTwo.setChecked)
cbTwo.toggled.connect(cbOne.setChecked)
#Connect allCB and allRO signals
cbsKeys = allCBList.keys()
for keys in cbsKeys:
for checkbox in allCBList[keys]:
keys.toggled.connect(checkbox.setChecked)
Only need help on turning off the All checkbox when the user selectively turns off the modular checkboxes now
If I'm understanding your data structure, I have a solution. Correct me if I'm wrong: allCBList is a dict (confusing name! :) ). Its keys are the all* checkboxes. And a value allCBList[key] is a list with checkboxes associated with that all checkbox. For your example structure it'll be something like this:
{ All1 : [checkbox1A, checkbox1B],
All2 : [checkbox2A, checkbox2B]}
Then what you need to is this: when a checkbox is toggled and it is in checked state, then you need to check the All* checkbox if all the other checkboxes are in checked state. Otherwise it will be unchecked.
for key, checkboxes in allCBList.iteritems():
for checkbox in checkboxes:
checkbox.toggled.connect(lambda checked, checkboxes=checkboxes, key=key: key.setChecked(checked and all(checkbox.isChecked() for checkbox in checkboxes))
I guess, this statement requires a bit of explanation:
lambda checked, checkboxes=checkboxes, key=key:
lambda creates the callable that is connected to the signal. toggled passes checkbox status, and it will be passed to checked variable. checkboxes=checkboxes and key=key parts pass the current values to checkboxes and key parameters of the lambda. (You need this because of the closure in lambdas)
Next comes:
key.setChecked(...)
We are setting the checked state of key which is the appropriate All* checkbox. And inside this:
checked and all(checkbox.isChecked() for checkbox in checkboxes)
all is True if everything inside is True, where we check every checkboxs state. And this will return True if all are checked (i.e. isChecked() returns True).
checked and ... part is there to short-circuit the all. If the current checkbox turns unchecked, then we don't need to check others. All* would be unchecked.
(PS: By the way, you don't need to get .keys() of a dict to iterate over keys. You can just iterate over the dict and it will iterate over its keys.)
Edit: Just to avoid chain reaction with All* checkboxes toggled by clicking any sub-checkboxes, it's necessary to change the signal for All* checkboxes to clicked, instead of toggled. So, the All* check boxes will affect other below them only in the case of user interaction.
In the end, your modified code will be:
# Connect checkbox pairs
# you just use the values
# change 'itervalues' to 'values' if you are on Python 3.x
for cbPair in cbPairs.itervalues():
cbOne = cbPair[0][0]
cbTwo = cbPair[1][0]
cbOne.toggled.connect(cbTwo.setChecked)
cbTwo.toggled.connect(cbOne.setChecked)
# Connect allCB and allRO signals
# change 'iteritems' to 'items' if you are on Python 3.x
for key, checkboxes in allCBList.iteritems():
for checkbox in checkboxes:
key.clicked.connect(checkbox.setChecked)
checkbox.toggled.connect(lambda checked, checkboxes=checkboxes, key=key: key.setChecked(checked and all(checkbox.isChecked() for checkbox in checkboxes))
Your problem is that your checkboxes are connecting the toggled signal and toggling their state in your connected slots so the signal is emitted again (so the slots are executed again...) and you get unpredictable results. Obviously that is not your wanted behavior. You can fix it in several ways:
by disconnecting the signals at the beginning of the slots and connecting them again at the end
by using some clever code that controls the re-emission of signals (I think this is what Avari's code does in a very compact way, but I'm not completely sure)
by using a clicked signal because it is not re-emitted when the checkbox state changes
Which approach you follow is up to you. The following code uses the third approach:
self.cbPair = {}
self.cbPair['0'] = (QtGui.QCheckBox('all1', parent),
QtGui.QCheckBox('all2', parent))
self.cbPair['1'] = (QtGui.QCheckBox('1a', parent),
QtGui.QCheckBox('1b', parent))
self.cbPair['2'] = (QtGui.QCheckBox('2a', parent),
QtGui.QCheckBox('2b', parent))
for v in self.cbPair.values():
for cb in v:
cb.clicked.connect(self.updateCB)
def updateCB(self):
cb = self.sender()
is_checked = cb.isChecked()
id = str(cb.text())
try:
# Update a whole column
column = int(id[-1]) - 1
rows = ('1', '2')
except ValueError:
# Update a row and the headers row
rows = (id[0], )
column = {'a': 1, 'b': 0}.get(id[-1])
if not is_checked:
for c in (0, 1):
self.cbPair['0'][c].setChecked(is_checked)
for r in rows:
self.cbPair[r][column].setChecked(is_checked)
Note that I'm using the checkboxes text as a UID from wich row and colum values are calculated. If you want to use different text labels for your checkboxes you may need to set the UIDs as attributes to every checkbox.

PyGtk - Activating a combo box

If I have a combo box in pyGTK and would like to set a list of strings and then on clicking on one activate a command how would I do it?
At the moment I have:
self.combo_key = gtk.Combo()
self.combo_key.set_popdown_strings(self.keys)
self.combo_key.entry.set_text(db.keys()[0])
self.combo_key.entry.connect("activate", self.key_sel)
But "activate" only calls after selection, and then by pressing enter. I'm also getting a deprecation warning for gtk.Combo() but cannot find any help on using gtk.ComboBoxEntry()
Any help guys?
Try using a gtk.ComboBox instead of gtk.Combo, since the latter is deprecated in favor of the former. To initialise, you can you code like:
liststore = gtk.ListStore(gobject.TYPE_STRING)
for key in self.keys:
liststore.append((key,))
combobox = gtk.ComboBox(liststore)
cell = gtk.CellRendererText()
combobox.pack_start(cell, True)
combobox.add_attribute(cell, 'text', 0)
Now you connect to the changed signal of the combobox and use its get_active() method to ask for the item that was selected.
As you might guess from this explanation, the ComboBox isn't exactly made for this purpose. You probably want to use gtk.Menu.

Categories

Resources