Kivy dynamic widget issue - python

Well, i'm a beginner using kivy framework, so i thought that someone here could help me.
My question is:
On my app, the user input a number n, then the app return n TextInput widgets. But how can i use the values inserted on each TextInput? The first part is easy to do, i did it by a list. (If someone know how to do it directly on kv file i would appreciate it). My issue is on second part, i need to use and manipulate these values (in TextInputs) later but i can't reach to them. I mean, i set up for each widget in the list an id, but i can't reach to .text attribute of them. Here is a piece of my code:
class FirstLayout(BoxLayout):
def next_layout(self):
self.clear_widgets()
secondLayout = Factory.SecondLayout()
number = NumericProperty()
# This 'number' variable came from another widget on kv file
textinput_list = []
# That list will receive as many TextInputs field as my variable 'number' said (by the loop for)
for i in range(int(self.number.text)):
textinput_list.append(TextInput())
textinput_list[i].id = "input" + str(i + 1)
# So for each textinput, i added a id named 'inputx' (where x is the number of the current
# iteration) my question resides here. How could i use those values (inside of each textinput
# on this list
# later? Because i'm not creating these widgets (TextInputs) on kv language so i don't know how to
# bind the ids for a new variable directly in .py file
secondLayout.container.add_widget(textinput_list[i])
self.add_widget(secondLayout)

If I understand your question right, you just have to make textinput_list a class variable. So this.
textinput_list = []
becomes
self.textinput_list = []
Lets say you have an object of FirstLayout called first. With first.textinput_list[0] you can access the first textinput and so on.
If you want to easily access the textinputs via id I would suggest using a dictionary, with the keys being the id's and the values being the inputs.

Related

Streamlit dynamic UI to generate dynamic input widgets depending on value from a different input widget

I want to open this post as I can't find anything on the official documentation from streamlit or any resources that mentioned how to do this. After some trial and error I have figured out a way, and will post the answer below. This is a function that in R shiny is called dynamic UI, here's the question.
How to generate dynamic input widgets depending on the value from a different input widget? For example see below picture, the numbers of text_input called Product Code i depends on the value from the number_input called Number of Products. So if there are x number of products, there will be x number of text_input generated dynamically. Moreover, the value inside the generated text_input can be extracted as well.
Here is one way to do this.
First, use list comprehension to store keys (variables to use to extract values from text_input later) and values (text_input).
Next, use key and value to set the attribute in a class.
The value from text_input labelled as product2 can be extracted using the attribute within the class using p.product2 for example.
import streamlit as st
number_of_products = st.sidebar.number_input(label="Number of Products",
min_value=0, max_value=20, value=1)
class Products:
pass
p = Products()
keys = [str("product"+str(x+1)) for x in range(number_of_products)]
values = [st.sidebar.text_input(label=f"Product Code {x+1}", value=0) for x in range(number_of_products)]
for key, value in zip(keys, values):
setattr(p, key, value)
# each key's value is stored in the class as an attribute
st.write(p.product2)
Using dictionary and exec command can also dynamically declare variables, but when the value inside the text_input is not a number, it will generate an error.
Generating dynamic input widget content is possible when using streamlit's session state. However, there is a potential drawback of streamlit refreshing the page upon input widget interaction.
One way to solve for this is to create multiple forms. For example, in your case you can create one form for "Number of Products", and update this value to the session state.
Next, you can create another form that takes in this "Number of Products" parameter and creates x number of input widgets.
import streamlit as st
with st.form("Number of Products"):
numProducts = st.number_input('Insert a number', key='numProducts')
submitForm = st.form_submit_button("Set Product Number")
if submitForm:
st.success("Please assign product codes below")
if 'numProducts' in st.session_state.keys():
with st.form("Product Codes"):
for i in range(st.session_state['numProducts']):
# insert text inputs with keys here
Hope this helps!

asigning ids in kivy on the python side

im using kivy. the what im trying to do is have and 'idea',a slider and a label containing the slider's current value in a row in a grid layout
now getting the layout is fine but getting the label to have a text value the same as the slider's current value is tricky. I'm trying to use string concation to refer to the label with the same number suffix as the slider that it is paired with.
I think the problem im having is that im trying to assign ids on the python side when they normally have to be done on the kv side. It's either that or the fact the ids i'm assigning are strings when kv would normally expect plain text. any help would be appreciated
class ScatterTextWidget(FloatLayout):
def run_me(self):
r=1
main_list=self.ids.main_list
main_list.clear_widgets()
main_list.height=0
for idea in imported_ideas:
main_list.add_widget(Label(text=idea,color=(0,0,0,1),id='idea_label_'+str(r)))
main_list.add_widget(Slider(id='Slider_'+str(r),min=0,max=10,value=5, step=1,on_value_pos=self.slider_slid(self)))
main_list.add_widget(Label(color=(0,0,0,1),id='value_label_'+str(r)))
value_label=self.ids['value_label_'+str(r)] # get this working and then apply the method into slider slid
value_label.text='xxx'
main_list.height+=35
r +=1
button_1=self.ids.button_1
button_1.text='Begin'
button_1.bind(on_press=self.begin)
def slider_slid(self,sender):
s=str(sender.id)
value_label=self.ids['value_label_'+str(s[12:])]
value_label.text=str(sender.value)
value_label=self.ids['value_label_'+str(s[12:])]
KeyError: 'value_label_'
self.ids only collects ids from children in the kv language rule of the widget. It doesn't know about widgets you added via python.
You don't need to use the id though. In this case you could keep e.g. a dictionary of id -> widget keys.
self.keys_dict = {}
for idea in imported_ideas:
new_widget = Label(color=(0,0,0,1),id='value_label_'+str(r)))
main_list.add_widget(new_widget)
self.keys_dict['value_label_' + str(r)] = new_widget
Then later you can access it with self.keys_dict['value_label_' + str(s[12:])] or whatever you like.
I suppose in practice you could also modify the actual ids dictionary in the same way, though I subjectively feel it is preferable to maintain your own dictionary with a name that represents its more specific contents.

PyQt : Mapping between table Widget and list widget

I was wondering of any function that can ease/solve above challenge. Basically, when a GUI (containing list and table widgets) starts. Whenever user click on each item in the list, the table should also responds to data corresponding to the list item. Refer to the following example for better understanding.
From time to time, the data in list or table can be removed, modified or added by user. I would also like to implement function to gather those. Currently, I am thinking of playing around python list and dict.
You can
use the QListWidget.itemClicked signal.
Connect it to a function, that takes an QListWidgetItem as argument.
Identify the item and act accordingly
Example
...
self.myListWidget.itemClicked.connect(self.showDataInTable)
....
def showDataInTable(item):
item_name = str(item.text()) # getting item name as python string
... # show data or do what ever you like
dataToShow = myDataDict[item_name]

dynamically asigning instances in Python/PyQt4

Ok, this might be a duplicate, but as I couldn't really get anything out of (possibly) similar questions, here is mine: I'm working on a small PyQt4 program where I can enter the name of a song in a QLineEdit and then add a QLabel of it beneath it. I want a button beside each one of these labels that deletes the label when clicked. Relevant code:
def Add(self):
self.rf= QtGui.QLabel(self.le1.text(),self)
self.rf.move(45,30)
self.rf.resize(450,30)
self.rf.show()
self.x = QtGui.QPushButton("X",self)
self.x.move(10,30)
self.x.resize(30,30)
self.x.show()
self.x.clicked.connect(self.Del)
def Del(self):
self.rf.close()
self.x.close()
Now, what I'm not understanding is how I can assign a different instance to each of these dynamically added Qlabels, in order to delete the specific one when the button is clicked.
The best idea I had was creating a variable containing a number that would change with each added QLabel, something like var = rf+str(num) and num = 0, then adding 1 to num for each QLabel and then using getattr for the instances, so getattr(self, var) = Qtgui.QLabel(...), which unfortunately gives me an error that I can't assign that value to the function. And I can't create a dictionary since I have to have different instances for that.
Any ideas would be highly appreciated, thanks a lot.
You could keep them all in a dict and then key that off of the label text. It also provides a quick way to check for duplicates.

Getting multiple wx widget values with Event Handling

CODE: http://pastebin.com/W4uXmazw
I would like to memorize how to get values from any wx widget with event handling after clicking a wx.Button.
In my program i have two fields, the new filename and the contents.
What are the steps i have to take in order to get the values from each field?
From there, i can use pythons f.open and f.write methods to complete my application.
Thanks!
If you want to get value of a widget, then you need to make that widget accessible throughout the entire class. To do that, you need to make the variable for the widget into an instance variable. So instead of adding the text control directly to the sizer, you'll want to do something like this:
self.newfilename = wx.TextCtrl(panel,-1), 0, wx.TOP, 5)
self.contents = wx.TextCtrl(panel,-1,size=(390,150),style = wx.TE_MULTILINE|wx.TE_PROCESS_TAB)
Then in your button's event handler, you can just do something like this:
valueOne = self.newfilename.GetValue()
contents = self.contents.GetValue()
The other way to do it would be to use your panel. If you use "self.panel", then you could grab all its children via its GetChildren method and then iterate over the list and use Python's "isinstance" builtin to check what kind of widget you're accessing. If you have set the widget's name, you can check that too.

Categories