Home > Back-end >  Attach kivy button image source to python class attribute
Attach kivy button image source to python class attribute

Time:01-08

I have been working on this problem for a long time and I cannot figure out a solution. I am making a game of tic tac toe, and I have created a gridlayout of 9 buttons. When a button is pressed, I want it to change from a placeholder image to an image of an x or an o. I have assigned the button image source as a python class attribute, and I can call a function so the attribute updates when the button is pressed. I know it's working because I get an output from the function every time a button is pressed. The problem is that the image does not update. I've looked everywhere for a solution but I can't find one.

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.gridlayout import GridLayout

class Game(GridLayout):
    empty = str('C:/Users/codin/Desktop/tictactoe/walpaper.png')
    states = ['button1', 'button2', 'button3', 'button4', 'button5',
              'button6', 'button7', 'button8', 'button9']
    button1 = empty
    button2 = empty
    button3 = empty
    button4 = empty
    button5 = empty
    button6 = empty
    button7 = empty
    button8 = empty
    button9 = empty

    def change_state(self, state):
        O = str('C:/Users/codin/Desktop/tictactoe/just_o.png')
        X = str('C:/Users/codin/Desktop/tictactoe/just_x.png')
        empty = str('C:/Users/codin/Desktop/tictactoe/walpaper.png')

        if getattr(self, state) == empty:
            setattr(self, state, X)
        elif getattr(self, state) == X:
            setattr(self, state, O)
        elif getattr(self, state) == O:
            setattr(self, state, X)
        else:
            setattr(self, state, empty)
        print(self.__dict__[state])

    pass


# Create an app class to do the handling
class TicTacToe(App):
    def build(self):
        return Game()


if __name__ == "__main__":
    TicTacToe().run()

.kv file:

<Game>
    cols: 3
    rows: 4

    Button:
        on_press: root.change_state(root.states[0])
        Image:
            id: button1
            source: root.button1
            center_x: self.parent.center_x
            center_y: self.parent.center_y

    Button:
        on_press: root.change_state(root.states[1])
        Image:
            id: button2
            source: root.button2
            center_x: self.parent.center_x
            center_y: self.parent.center_y

more buttons below

I am fully aware that I can write a function for every button, and change the image source via self.ids.image_id.source in the python file, but I'm trying to keep it short. Any help is greatly appreciated.

CodePudding user response:

Something like this,

In your Game class,

img_O = StringProperty('C:/Users/codin/Desktop/tictactoe/just_o.png')
img_X = StringProperty('C:/Users/codin/Desktop/tictactoe/just_x.png')
img_empty = StringProperty('C:/Users/codin/Desktop/tictactoe/walpaper.png')

Then in kv,

    Button:
        on_press: button1.source = root.img_X if button1.source == root.img_empty else ... 
        Image:
            id: button1

CodePudding user response:

I was able to figure it out using a combination of ApuCoder's answer and information from the kivy.lang module outline here: https://kivy.org/doc/stable/api-kivy.lang.html

My python code is very short, simple variables assigned to str() filepath for images in a game class, and an app class to compile:

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.gridlayout import GridLayout


# class for image sources and layout
class Game(GridLayout):
    O = str('just_o.png')
    X = str('just_x.png')
    empty = str('wallpaper.png')


# App class to do the handling
class TicTacToe(App):
    def build(self):
        return Game()


if __name__ == "__main__":
    TicTacToe().run()

In kv, you can write conditional statements. The requirements are that each statement is on its own line. if/elif/else as well as for and while loops are all allowed.

<Game>
    cols: 3
    rows: 3

    Button:
        on_press:
            if button1.source == root.empty: button1.source = root.X
            elif button1.source == root.X: button1.source = root.O
            else: button1.source = root.X
        Image:
            id: button1
            source: root.empty
            center_x: self.parent.center_x
            center_y: self.parent.center_y

This code basically says, "image source assigned at start, on_press if the image is this, change it to that, and vice versa"

This is likely the least amount of code needed to accomplish this task.

  •  Tags:  
  • Related