Kivy add widget at the end of layout - python

In the kivy doc there is the function add_widget(widget, index=0, canvas=None) and we must be able to set an index to position the widget.
index: int, defaults to 0
Index to insert the widget in the list. Notice that the default of 0 means the widget is inserted at the beginning of the list and will
thus appear under the other widgets. For a full discussion on the
index and widget hierarchy, please see the Widgets Programming Guide.
So first either index I try nothing change on the widget order. And then i would like to insert my widget at the end of the list and not at the beginning.
If you have some idea :)

I found the solution:
self.add_widget(your_widget, len(self.children))
If you print self.children you will see the object added at the end of the list and it will reverse the display order on the view.

Related

How to modify tags in Tkinter Canvas by events

I am programmatically adding tag_binds to all objects on my canvas that have the tag "tag":
self.canvas.tag_bind("tag","<Button 2>",self.tag_highlight)
self.canvas contains all the objects that the user has added (all tagged with "tag".
However, in tag_highlight, I would simply like to have a handle on the actual element within the canvas. event.widget doesn't seem to help, its just a tuple of 4 floats that I cannot link to any canvas item. I have tried to use the following, to no avail:
self.canvas.find_closest(event.x,event.y)
What I would like to do, is have a handle like:
t=self.canvas.getitem(event.widget)
so that I can use it for example in:
self.canvas.Move(t,30,20)
print self.canvas.coords(t)
and so forth.
Maybe I am just missing the obvious?
You can use the tag "current", which refers to the "current" object. The current object is described like this in the official tk documentation:
The tag current is managed automatically by Tk; it applies to the
current item, which is the topmost item whose drawn area covers the
position of the mouse cursor (different item types interpret this in
varying ways; see the individual item type documentation for details).
If the mouse is not in the canvas widget or is not over an item, then
no item has the current tag.

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.

Kivy dynamic widget issue

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.

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.

Refreshing panel contents in wxPython

What is the approach to update widgets in a wxPanel based on events from other controls on same panel?
Scenario 1 is updating the list of a comboBox based on what has been selected from another comboBox , where both are in same panel.
Scenario 2 is showing a new control/widget in a panel based on an event.
Basically creating new controls is easy but I dont know how to refresh/update my panel so immedialtly shows them.
Scenario 1
To change the choices of a combobox self.cbx you can use any of the following methods:
self.cbx.SetItems(choices) where choices is the full list of choices.
self.cbx.SetString(n, string) that sets the string at position n.
InsertItems(items, pos) Inserts the list of strings in the items argument into the list box before the position in the pos argument.
Note that the method Set(choices) of listboxes does not exist for the list in comboboxes. You must use SetItems(choices) instead (this is not clearly indicated in some textbooks).
If you want these changes to occur as a result of a selection in another combobox self.cbx_1 , just get the event (self.Bind(wx.EVT_COMBOBOX, on_combo_1, self.cbx_1)) of the first combobox, process your data as you like in the corresponding self.on_combo method and use one of the above methods to modify the second combobox.
For example:
def on_combo_1(self, evt):
"append cbx_1 selection to cbx if not already in cbx"
selection = self.cbx_1.GetStringSelection()
cbx_choices = self.cbx.GetItems()
if selection not in cbx_choices:
cbx_choices.append(selection)
self.cbx.SetItems(cbx_choices)
The fact the comboboxes are in the same or different panel is irrelevant for that.
Scenario 2
Normally you put your widgets inside sizers. To hide or made visible elements on the sizer you call the methods Show, Hide or Layout:
Show(self, item, show=True, recursive=false)
Shows or hides an item managed by the sizer. To make a sizer item disappear or reappear, use Show followed by Layout. The item parameter can be either a window, a sizer, or the zero-based index of the item. Use the recursive parameter to show or hide an item in a subsizer. Returns True if the item was found.
Hide(self, item, recursive)
A convenience method for Show (item, False, recursive).
Layout(self)
This method will force the recalculation and layout of the items controlled by the sizer using the current space allocated to the sizer. Normally this is called automatically from the owning window's EVT_SIZE handler, but it is also useful to call it from user code when one of the items in a sizer change size, or items are added or removed.
References: wxPython in Action, Noel Rappin and Robin Dunn
For scenario one, you'd do something like the following (assuming the first combobox is bound to its EVT_COMBOBOX:
value = self.cboOne.GetValue()
if value == "something":
self.cboTwo.SetItems(someList)
For showing a new widget, you could create it and then use Show()/Hide() as necessary. If the widget is in a sizer, then use the Sizer's Append or Insert methods. It also has a Detach method that can be used to hide widgets or you just call Hide itself. See the documentation for more information: http://www.wxpython.org/docs/api/wx.Sizer-class.html

Categories