I decided to try out Python and it's been fun so far. However while messing around with tkinter I encountered a problem which I haven't been able to solve for hours. I've read some things and tried different stuff but nothing works. I've got the code so far that I think the program should run fine. Except for the fact that I can't make it loop and thus update automatically.
So my question is: how can I call a function with tkinters loop options in an infite loop fashion?
Simple game of life: I wrote basicly 2 classes. A Matrix which stores and handles the single cells and the game itself which utilizes the matrix class through some game logic and basic user input.
First the game class as there is my loop problem:
from tkinter import *
from ButtonMatrix import *
class Conway:
def __init__(self, master, size = 20, cell_size = 2):
self.is_running = True
self.matrix = ButtonMatrix(master,size,cell_size)
self.matrix.randomize()
self.matrix.count_neighbours()
self.master = master
# playbutton sets boolean for running the program in a loop
self.playbutton = Button(master, text = str(self.is_running), command = self.stop)
self.playbutton.grid(row = 0 , column = size +1 )
#Test button to trigger the next generation manually. Works as itended.
self.next = Button(master, text="next", command = self.play)
self.next.grid(row = 1, column = size +1)
def play(self): # Calculates and sets the next generation. Intended to be used in a loop
if self.is_running:
self.apply_ruleset()
self.matrix.count_neighbours()
self.apply_colors()
def apply_ruleset(self):
#The ruleset of conways game of life. I wish i knew how to adress each element
#without using these two ugly loops all the time
size = len(self.matrix.cells)
for x in range (size):
for y in range (size):
if self.cell(x,y).is_alive():
if self.cell(x,y).neighbours < 2 or self.cell(x,y).neighbours > 3:
self.cell(x,y).toggle()
if not self.cell(x,y).is_alive() and self.cell(x,y).neighbours == 3:
self.cell(x,y).toggle()
def apply_colors(self): #Some flashy colors just for fun
size = len(self.matrix.cells)
for x in range (size):
for y in range (size):
if self.cell(x,y).is_alive():
if self.cell(x,y).neighbours < 2 or self.cell(x,y).neighbours > 3:
self.cell(x,y).button.configure(bg = "chartreuse3")
if not self.cell(x,y).is_alive() and self.cell(x,y).neighbours == 3:
self.cell(x,y).button.configure(bg = "lightgreen")
def cell(self,x,y):
return self.matrix.cell(x,y)
def start (self): #start and stop set the boolean for the loop. They work and switch the state properly
self.is_running = True
self.playbutton.configure(text=str(self.is_running), command =self.stop)
def stop (self):
self.is_running = False
self.playbutton.configure(text=str(self.is_running), command =self.start)
#Test program. I can't make the loop work. Manual update via next button works however
root = Tk()
conway = Conway(root)
root.after(1000, conway.play())
root.mainloop()
The Matrix (only for interested readers):
from tkinter import *
from random import randint
class Cell:
def __init__(self,master, cell_size = 1):
self.alive = False
self.neighbours = 0
# initializes a squares shaped button that fills the grid cell
self.frame = Frame(master, width= cell_size*16, height = cell_size*16)
self.button = Button(self.frame, text = self.neighbours, command = self.toggle, bg ="lightgray")
self.frame.grid_propagate(False)
self.frame.columnconfigure(0, weight=1)
self.frame.rowconfigure(0,weight=1)
self.button.grid(sticky="wens")
def is_alive(self):
return self.alive
def add_neighbour(self):
self.neighbours += 1
def toggle (self):
if self.is_alive() :
self.alive = False
self.button.configure( bg = "lightgray")
else:
self.alive = True
self.button.configure( bg = "green2")
class ButtonMatrix:
def __init__(self, master, size = 3, cell_size = 3):
self.master = master
self.size = size
self.cell_size = cell_size
self.cells = []
for x in range (self.size):
row = []
self.cells.append(row)
self.set_cells()
def cell(self, x, y):
return self.cells[x][y]
def set_cells(self):
for x in range (self.size):
for y in range (self.size):
self.cells[x] += [Cell(self.master, self.cell_size)]
self.cell(x,y).frame.grid(row=x,column=y)
def count_neighbours(self): # Checks 8 sourounding neighbours for their stats and sets a neighbour counter
for x in range(self.size):
for y in range(self.size):
self.cell(x,y).neighbours = 0
if y < self.size-1:
if self.cell(x,y+1).is_alive(): self.cell(x,y).add_neighbour() # Right
if x > 0 and self.cell(x-1,y+1).is_alive(): self.cell(x,y).add_neighbour() #Top Right
if x < self.size-1 and self.cell(x+1,y+1).is_alive(): self.cell(x,y).add_neighbour() #Bottom Right
if x > 0 and self.cell(x-1,y).is_alive(): self.cell(x,y).add_neighbour()# Top
if x < self.size-1 and self.cell(x+1,y).is_alive():self.cell(x,y).add_neighbour() #Bottom
if y > 0:
if self.cell(x,y-1).is_alive(): self.cell(x,y).add_neighbour() # Left
if x > 0 and self.cell(x-1,y-1).is_alive(): self.cell(x,y).add_neighbour() #Top Left
if x < self.size-1 and self.cell(x+1,y-1).is_alive(): self.cell(x,y).add_neighbour() #Bottom Left
self.cell(x,y).button.configure(text = self.cell(x,y).neighbours)
def randomize (self):
for x in range(self.size):
for y in range(self.size):
if self.cell(x,y).is_alive(): self.cell(x,y).toggle()
rando = randint(0,2)
if rando == 1: self.cell(x,y).toggle()