Layouts and Widgets
A Match-3 board is fundamentally a grid, so before writing any game logic, this lesson covers the Kivy widgets that will hold it: layouts, which arrange other widgets, and buttons, which will represent each gem.
Layouts arrange widgets, they don't draw anything themselves
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
class DemoApp(App):
def build(self):
root = BoxLayout(orientation='vertical')
root.add_widget(Label(text='Score: 0', font_size=24))
root.add_widget(Button(text='Shuffle'))
return root
if __name__ == '__main__':
DemoApp().run()
BoxLayoutstacks its children in a single row or column,orientation='vertical'stacks them top to bottom.add_widget(...)is how every widget, layout or not, gets a child added to it. Layouts just also decide where their children end up.
GridLayout: the board itself
An 8×8 Match-3 board is a perfect fit for GridLayout, which arranges children into a fixed number of columns (and, implicitly, however many rows the number of children requires):
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
class BoardDemoApp(App):
def build(self):
grid = GridLayout(cols=8)
for i in range(8 * 8):
grid.add_widget(Button(text=str(i)))
return grid
if __name__ == '__main__':
BoardDemoApp().run()
Setting cols=8 and then adding 64 buttons is all it takes, GridLayout automatically wraps to a new row every 8 widgets, giving you an 8×8 grid with zero manual positioning math.
Buttons as gems
Each cell in the board will be a Button, gems don't need any text, just a solid background color, which Kivy exposes through two properties together:
from kivy.uix.button import Button
gem = Button(text='', background_normal='', background_color=(0.9, 0.2, 0.2, 1)) # solid red
background_coloris an RGBA tuple, each channel from0to1(not 0-255 like most other tools).background_normal=''is required to actually see that color, by default Kivy'sButtondraws its built-in gray button-image on top ofbackground_color, setting it to an empty string turns that image off so the flat color shows through.
Responding to taps
def on_gem_pressed(instance):
print('tapped a gem!')
gem.bind(on_press=on_gem_pressed)
bind(on_press=...) connects a function to the button's press event, Kivy calls it with the widget instance itself as the only argument, which is exactly how the next lesson will know which gem was tapped.
TIP
size_hint (a tuple like (1, 1)) controls how much of the available space a widget claims relative to its siblings, the default (1, 1) already makes every gem button fill its grid cell evenly, so you won't need to touch it for this project.