Empty list of ids in a Kivy GridLayout
Question:
I am trying to develop a toy app with Kivy.
So far I have a grid of 9 x 9 TextInput
widgets that have been added programmatically to a GridLayout
(I refer to it by the id “grid” in my kv file).
For some reason, although I am assigning an id to each of the TextInput
widgets, the ids dictionary that I get in the get_widget_from_id
is empty.
What am I missing?
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
class SudokuGame(Widget):
# Initialize the grid of text inputs
def __init__(self, **kwargs):
super().__init__(**kwargs)
grid = self.ids["grid"]
for i in range(81):
row = i // 9
col = i % 9
grid.add_widget(TextInput(id = str(row) + "-" + str(col)))
# Try to retrieve one of the widgets from its id
def get_widget_from_id(self, *args):
print(self.ids["grid"].ids)
class SudokuApp(App):
def build(self):
game = SudokuGame()
Clock.schedule_once(game.get_widget_from_id)
return game
if __name__ == '__main__':
SudokuApp().run()
sudoku.kv
#:kivy 1.9.1
<TextInput@SudokuGame>:
text: ""
font_size: 0.7 * self.width
padding: 0.3 * self.width, (self.height - self.line_height) / 2
input_filter: lambda text, from_undo: text if ( 0 < int(text) < 10 and len(self.text) == 0 ) else ""
multiline: False
cursor_color: [0, 0, 0, 0]
<SudokuGame>:
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: "vertical"
size: root.size
GridLayout:
id: grid
rows: 9
cols: 9
line_size: 6
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.x + self.width / 3 - self.line_size / 2, self.y
size: self.line_size, self.height
Rectangle:
pos: self.x + 2 * self.width / 3 - self.line_size / 2, self.y
size: self.line_size, self.height
Rectangle:
pos: self.x, self.y + self.height / 3 - self.line_size / 2
size: self.width, self.line_size
Rectangle:
pos: self.x, self.y + 2 * self.height / 3 - self.line_size / 2
size: self.width, self.line_size
BoxLayout:
orientation: "horizontal"
size_hint: 1, 0.1
Button:
text: "Solve"
Button:
text: "Clear"
Answers:
The ids dict is populated only from kv ids. The Kivy property that widgets have is actually unrelated, and I’m not sure if it’s used for anything.
Sticking this in there might fix your problems
self.ids = {child.id:child for child in self.children}
Answer from Lawrence DU did not solve my problem, but spinned my gears.
My problem was solved by this messy code, test it in your situation:
def update_ids(self, **kw):
def recursion(node, parent):
parent.ids = {**parent.ids, **node.ids}
if node.id:
parent.ids[node.id] = node
if node.children:
for child in node.children:
parent.ids = {**parent.ids, **child.ids}
recursion(child, parent)
else:
pass
for c in self.children:
recursion(c, self)
Instead use this.
def get_id(self, instance):
for id, widget in instance.parent.parent.ids.items():
if widget.__self__ == instance:
return id
def add_label(self):
for label_id in range(self._label_count):
label_widget = MDLabel(
text="0",
font_size='44',
text_color="#fefbff",
)
self.root.ids.box_label.add_widget(label_widget)
self.root.ids[f"label_{label_id}"] = label_widget
I am trying to develop a toy app with Kivy.
So far I have a grid of 9 x 9 TextInput
widgets that have been added programmatically to a GridLayout
(I refer to it by the id “grid” in my kv file).
For some reason, although I am assigning an id to each of the TextInput
widgets, the ids dictionary that I get in the get_widget_from_id
is empty.
What am I missing?
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
class SudokuGame(Widget):
# Initialize the grid of text inputs
def __init__(self, **kwargs):
super().__init__(**kwargs)
grid = self.ids["grid"]
for i in range(81):
row = i // 9
col = i % 9
grid.add_widget(TextInput(id = str(row) + "-" + str(col)))
# Try to retrieve one of the widgets from its id
def get_widget_from_id(self, *args):
print(self.ids["grid"].ids)
class SudokuApp(App):
def build(self):
game = SudokuGame()
Clock.schedule_once(game.get_widget_from_id)
return game
if __name__ == '__main__':
SudokuApp().run()
sudoku.kv
#:kivy 1.9.1
<TextInput@SudokuGame>:
text: ""
font_size: 0.7 * self.width
padding: 0.3 * self.width, (self.height - self.line_height) / 2
input_filter: lambda text, from_undo: text if ( 0 < int(text) < 10 and len(self.text) == 0 ) else ""
multiline: False
cursor_color: [0, 0, 0, 0]
<SudokuGame>:
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: "vertical"
size: root.size
GridLayout:
id: grid
rows: 9
cols: 9
line_size: 6
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.x + self.width / 3 - self.line_size / 2, self.y
size: self.line_size, self.height
Rectangle:
pos: self.x + 2 * self.width / 3 - self.line_size / 2, self.y
size: self.line_size, self.height
Rectangle:
pos: self.x, self.y + self.height / 3 - self.line_size / 2
size: self.width, self.line_size
Rectangle:
pos: self.x, self.y + 2 * self.height / 3 - self.line_size / 2
size: self.width, self.line_size
BoxLayout:
orientation: "horizontal"
size_hint: 1, 0.1
Button:
text: "Solve"
Button:
text: "Clear"
The ids dict is populated only from kv ids. The Kivy property that widgets have is actually unrelated, and I’m not sure if it’s used for anything.
Sticking this in there might fix your problems
self.ids = {child.id:child for child in self.children}
Answer from Lawrence DU did not solve my problem, but spinned my gears.
My problem was solved by this messy code, test it in your situation:
def update_ids(self, **kw):
def recursion(node, parent):
parent.ids = {**parent.ids, **node.ids}
if node.id:
parent.ids[node.id] = node
if node.children:
for child in node.children:
parent.ids = {**parent.ids, **child.ids}
recursion(child, parent)
else:
pass
for c in self.children:
recursion(c, self)
Instead use this.
def get_id(self, instance):
for id, widget in instance.parent.parent.ids.items():
if widget.__self__ == instance:
return id
def add_label(self):
for label_id in range(self._label_count):
label_widget = MDLabel(
text="0",
font_size='44',
text_color="#fefbff",
)
self.root.ids.box_label.add_widget(label_widget)
self.root.ids[f"label_{label_id}"] = label_widget