UPDATE:
Ryan P's answer provided a solution. However, I made this decision and slightly changed it, throwing away all the data that was not correctly initialized in the on_enter method for the RootWidget Screen. It works well.
The My RootWidget class has been subclassed by Widget until today, and I had no problems accessing them to get the value of the "grid". However, I just changed it for the Screen subclass, and now it says that the identifiers are empty for some reason ... The screen really has identifiers and all that, but for some reason it does not register this I assigned a GridLayout to id '' grid '' in the kv file. Can someone tell me why?
Track:
[INFO ] [Logger ] Record log in /home/yerman/.kivy/logs/kivy_14-11-13_201.txt [INFO ] Kivy v1.9.0-dev [INFO ] [Python ] v2.7.6 (default, Mar 22 2014, 22:59:56) [GCC 4.8.2] [INFO ] [Factory ] 172 symbols loaded [INFO ] [Image ] Providers: img_tex, img_dds, img_pygame, img_pil, img_gif (img_sdl2, img_ffpyplayer ignored) [INFO ] [Window ] Provider: pygame(['window_egl_rpi'] ignored) [WARNING] [WinPygame ] Video: failed (multisamples=2) [WARNING] [WinPygame ] trying without antialiasing [INFO ] [GL ] OpenGL version <2.1 Mesa 10.1.3> [INFO ] [GL ] OpenGL vendor <Intel Open Source Technology Center> [INFO ] [GL ] OpenGL renderer <Mesa DRI Intel(R) Ironlake Mobile > [INFO ] [GL ] OpenGL parsed version: 2, 1 [INFO ] [GL ] Shading version <1.20> [INFO ] [GL ] Texture max size <8192> [INFO ] [GL ] Texture max units <16> [INFO ] [Window ] virtual keyboard not allowed, single mode, not docked [INFO ] [Text ] Provider: pygame(['text_sdl2'] ignored) {} #<<< note the emtpy ids I printed out Traceback (most recent call last): File "main.py", line 169, in <module> MineSweeperApp().run() File "/usr/lib/python2.7/dist-packages/kivy/app.py", line 799, in run root = self.build() File "main.py", line 163, in build return Manager() File "/usr/lib/python2.7/dist-packages/kivy/uix/screenmanager.py", line 844, in __init__ super(ScreenManager, self).__init__(**kwargs) File "/usr/lib/python2.7/dist-packages/kivy/uix/floatlayout.py", line 66, in __init__ super(FloatLayout, self).__init__(**kwargs) File "/usr/lib/python2.7/dist-packages/kivy/uix/layout.py", line 66, in __init__ super(Layout, self).__init__(**kwargs) File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 269, in __init__ Builder.apply(self) File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1837, in apply self._apply_rule(widget, rule, rule) File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1942, in _apply_rule child = cls(__no_builder=True) File "main.py", line 43, in __init__ self.grid = self.ids["grid"] KeyError: 'grid'
kv file:
#:kivy 1.8.0 <RootWidget>: GridLayout: id: grid size: root.size cols: root.sides <Blank>: background_color: 1, 1, 1, 1 background_disabled_down: "kivy_white_bg.png" on_press: self.parent.parent.sweep(self) <Mine>: background_color: 1, 1, 1, 1 background_disabled_down: "kivy_white_bg.png" on_press: self.parent.parent.sweep(self) <TryAgain>: anchor_x: 'center' anchor_y: 'center' BoxLayout: size: root.size orientation: 'vertical' padding_bottom: '20dp' Label: font_size: '20dp' text: root.text BoxLayout: size_hint: 1, .3 spacing: 10 padding: 10 Button: size_hint: .4, 1 font_size: '20dp' text: "yes" on_press: app.stop(); app.run() Button: size_hint: .4, 1 font_size: '20dp' text: "no" on_press: root.quit() <Menu>: GridLayout: rows: 2 Button: text: "8x8" on_press: root.manager.current = 'game_screen' Button: text: "16x16" on_press: root.manager.current = 'game_screen' Button: text: "30x16" on_press: root.manager.current = 'game_screen' Button: text: "custom" on_press: root.manager.current = 'game_screen' <Manager>: id: _manager menu: menu game: game current: menu_screen Menu: id: menu manager: _manager name: 'menu_screen' RootWidget: id: game manager: _manager name: 'game_screen'
main.py:
#!/usr/bin/env python from random import sample import sys import kivy kivy.require('1.8.0') from kivy.app import App from kivy.core.window import Window from kivy.uix.widget import Widget from kivy.uix.button import Button from kivy.properties import NumericProperty, ListProperty, StringProperty, ObjectProperty from kivy.uix.gridlayout import GridLayout from kivy.uix.modalview import ModalView from kivy.uix.screenmanager import ScreenManager, Screen from kivy.clock import Clock class Blank(Button): index = ListProperty([0, 0]) count = NumericProperty(0) def __init__(self, **kwargs): super(Blank, self).__init__(**kwargs) class Mine(Button): index = ListProperty([0, 0]) count = NumericProperty(0) # not really necessary def __init__(self, **kwargs): super(Mine, self).__init__(**kwargs) class RootWidget(Screen): sides = NumericProperty(10) mine_count = NumericProperty(20) def __init__(self, **kwargs): super(RootWidget, self).__init__(**kwargs) self.grid = self.ids["grid"] # generate random mine indices mines = sample(xrange(self.sides**2), self.mine_count) x, y = -1, 0 for i in xrange(self.sides**2): if x == self.sides - 1: x = 0 y += 1 else: x += 1 if i not in mines: b = Blank(index=[x, y]) else: b = Mine(index=[x, y]) self.grid.add_widget(b) # record mine, blank and safe blank indices self.all_btns = [c.index for c in self.grid.children] self.mines = [c.index for c in self.grid.children if isinstance(c, Mine)] self.blanks = [c.index for c in self.grid.children if isinstance(c, Blank)] # a safe blank has no adjacent mines self.safe_blanks = [c.index for c in self.grid.children if self.is_safe(c)] # give each btn an 'adjacent mines count' for x, y in self.all_btns: btn = self.get_child_by_index([x, y]) for index in self.field(x, y): if index in self.mines: btn.count += 1 def field(self, x, y): """ the minefield surrounding a btn """ field = [[x-1, y], [x+1, y], [x, y+1], [x, y-1], [x+1, y+1], [x-1, y-1], [x+1, y-1], [x-1, y+1]] get = self.get_child_by_index return [i for i in field if i in self.all_btns and get(i).disabled == False] def sweep(self, instance): instance.disabled = True if instance.index in self.mines: print "Boom!" # It a mine! You lose instance.text = "Boom!" self.game_over() pressed = sum(1 for c in self.grid.children if c.disabled == True) print pressed if self.sides**2 - pressed == self.mine_count: self.game_over(win=True) if instance.count > 0: instance.text = str(instance.count) instance.disabled = True return else: x, y = instance.index for index in self.field(x, y): if index not in self.mines: blank = self.get_child_by_index(index) blank.disable = True if blank.count > 0: blank.text = str(blank.count) self.sweep(blank) def is_safe(self, btn): x, y = btn.index for index in self.field(x, y): if index in self.mines: return False return True def get_child_by_index(self, index): for child in self.grid.children: if child.index == index: return child def game_over(self, q=False, win=False): if q == True: sys.exit() if win == True: result = "Win" elif win == False: result = "lost" view = TryAgain( size_hint = (None, None), width = self.width/2, height = self.height/2, center = self.center, text = "You {}! Try Again?".format(result)) view.open() class TryAgain(ModalView): text = StringProperty('') def quit(self): sys.exit() class Menu(Screen): pass class Manager(ScreenManager): menu = ObjectProperty(None) game = ObjectProperty(None) class MineSweeperApp(App): def build(self): return Manager() if __name__ == "__main__": MineSweeperApp().run()