So I have this maze code, and it's really cool but when you play you move around a red square. Is there a way that I can change this red square that moves around into an image, instead of where it says return "red" I want to put this personaje.gif file so a little person can appear moving through the maze.
this is my code:
import random
import Tkinter as tk
import sys
from PIL import Image, ImageTk
class Application(tk.Frame):
def __init__(self, width=600, height=600, size=600):
tk.Frame.__init__(self)
self.maze = Maze(width, height)
personaje = Image.open("personaje.gif")
photo = ImageTk.PhotoImage(personaje)
self.size = size
self.steps = 0
self.grid()
self.create_widgets()
self.draw_maze()
self.create_events()
def create_widgets(self):
width = self.maze.width * self.size
height = self.maze.height * self.size
self.canvas = tk.Canvas(self, width=width, height=height)
self.canvas.grid()
self.status = tk.Label(self)
self.status.grid()
def draw_maze(self):
for i, row in enumerate(self.maze.maze):
for j, col in enumerate(row):
x0 = j * self.size
y0 = i * self.size
x1 = x0 + self.size
y1 = y0 + self.size
color = self.get_color(x=j, y=i)
id = self.canvas.create_rectangle(x0, y0, x1, y1, width=0, fill=color)
if self.maze.start_cell == (j, i):
self.cell = id
self.canvas.tag_raise(self.cell) # bring to front
self.status.config(text='Movidas mínimas: %d' % self.maze.steps)
def create_events(self):
self.canvas.bind_all('<KeyPress-Up>', self.move_cell)
self.canvas.bind_all('<KeyPress-Down>', self.move_cell)
self.canvas.bind_all('<KeyPress-Left>', self.move_cell)
self.canvas.bind_all('<KeyPress-Right>', self.move_cell)
def move_cell(self, event):
if event.keysym == 'Up':
if self.check_move(0, -1):
self.canvas.move(self.cell, 0, -self.size)
self.steps += 1
if event.keysym == 'Down':
if self.check_move(0, 1):
self.canvas.move(self.cell, 0, self.size)
self.steps += 1
if event.keysym == 'Left':
if self.check_move(-1, 0):
self.canvas.move(self.cell, -self.size, 0)
self.steps += 1
if event.keysym == 'Right':
if self.check_move(1, 0):
self.canvas.move(self.cell, self.size, 0)
self.steps += 1
args = (self.steps, self.maze.steps)
self.status.config(text='Movimientos %d/%d' % args)
self.check_status()
def check_move(self, x, y):
x0, y0 = self.get_cell_coords()
x1 = x0 + x
y1 = y0 + y
return self.maze.maze[y1][x1] == 0
def get_cell_coords(self):
position = self.canvas.coords(self.cell)
x = int(position[0] / self.size)
y = int(position[1] / self.size)
return (x, y)
def check_status(self):
if self.maze.exit_cell == self.get_cell_coords():
args = (self.steps, self.maze.steps)
self.status.config(text='Resuelto en %d/%d movidas!' % args)
def get_color(self, x, y):
if self.maze.start_cell ==(x,y):
return "red"
if self.maze.exit_cell == (x, y):
return 'green'
if self.maze.maze[y][x] == 1:
return 'black'
class Maze(object):
def __init__(self, width=21, height=21, exit_cell=(1, 1)):
self.width = width
self.height = height
self.exit_cell = exit_cell
self.create()
def create(self):
self.maze = [[1] * self.width for _ in range(self.height)] # full of walls
self.start_cell = None
self.steps = None
self.recursion_depth = None
self._visited_cells = []
self._visit_cell(self.exit_cell)
def _visit_cell(self, cell, depth=0):
x, y = cell
self.maze[y][x] = 0 # remove wall
self._visited_cells.append(cell)
neighbors = self._get_neighbors(cell)
random.shuffle(neighbors)
for neighbor in neighbors:
if not neighbor in self._visited_cells:
self._remove_wall(cell, neighbor)
self._visit_cell(neighbor, depth+1)
self._update_start_cell(cell, depth)
def _get_neighbors(self, cell):
x, y = cell
neighbors = []
# Left
if x - 2 > 0:
neighbors.append((x-2, y))
# Right
if x + 2 < self.width:
neighbors.append((x+2, y))
# Up
if y - 2 > 0:
neighbors.append((x, y-2))
# Down
if y + 2 < self.height:
neighbors.append((x, y+2))
return neighbors
def _remove_wall(self, cell, neighbor):
x0, y0 = cell
x1, y1 = neighbor
# Vertical
if x0 == x1:
x = x0
y = (y0 + y1) / 2
# Horizontal
if y0 == y1:
x = (x0 + x1) / 2
y = y0
self.maze[y][x] = 0 # remove wall
def _update_start_cell(self, cell, depth):
if depth > self.recursion_depth:
self.recursion_depth = depth
self.start_cell = cell
self.steps = depth * 2 # wall + cell
def show(self, verbose=False):
MAP = {0: ' ', # path
1: '#', # wall
2: 'B', # exit
3: 'A', # start
}
x0, y0 = self.exit_cell
self.maze[y0][x0] = 2
x1, y1 = self.start_cell
self.maze[y1][x1] = 3
for row in self.maze:
print ' '.join([MAP[col] for col in row])
if verbose:
print "Steps from A to B:", self.steps
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser(description="Random maze game")
parser.add_option('-W', '--width', type=int, default=43)
parser.add_option('-H', '--height', type=int, default=43)
parser.add_option('-s', '--size', type=int, default=11,
help="cell size")
args, _ = parser.parse_args()
for arg in ('width', 'height'):
if getattr(args, arg) % 2 == 0:
setattr(args, arg, getattr(args, arg) + 1)
print "Warning: %s must be odd, using %d instead" % (arg, getattr(args, arg))
sys.setrecursionlimit(5000)
app = Application(args.width, args.height, args.size)
app.master.title('Maze game')
app.mainloop()
Related
I am trying to create a "Basketball game" using python and tkinter and I'm having trouble making my animation work. When I press the spacebar the basketball appears but it doesnt go upwards. I tried placing self.status.shoot == Status.shoot in multiple areas but the animation doesn't start once I hit the spacebar, instead the image of the basketball simply appears without movement. Any thoughts?
Here is my code:
model.py
import enum,math,random,time
# sizes of each images (pixels)
from Projects.MVC import controller
backboard1_height = 150
backboard1_width = 150
backboard2_height = 150
backboard2_width = 150
backboard3_height = 150
backboard3_width = 150
bg_height = 750
bg_width = 1000
floor_height = 180
floor_width = 1000
player_height = 250
player_width = 250
ball_height = 50
ball_width = 50
class Status(enum.Enum):
run = 1
pause = 2
game_over = 3
terminate = 4
shoot = 5
class HorizontalDirection(enum.IntEnum):
left = -1
none = 0
right = 1
class VerticalDirection(enum.IntEnum):
up = -1
none = 0
down = 1
class ImgDirection(enum.Enum):
left = -1
right = 1
class GameModel:
def __init__(self):
self.status = Status.pause
self.elements = []
self.next_time = time.time() # when we try drop a ball
# create elements
self.bg = Background(bg_width / 2, bg_height / 2)
self.floor = Floor(bg_width / 2, bg_height - (floor_height / 2))
self.backboard1 = Backboard1(bg_width / 2, player_height / 2)
self.backboard2 = Backboard2(bg_width / 10, player_height / 2)
self.backboard3 = Backboard3((bg_width / 2) + 400, player_height / 2)
self.player = Player(bg_width / 2, bg_height - floor_height )
self.text = TextInfo(80, 30)
self.init_elements()
def init_elements(self): # layer images on top of each other in order for every thing to be seen
self.elements = []
self.elements.append(self.bg)
self.elements.append(self.floor)
self.elements.append(self.backboard1)
self.elements.append(self.backboard2)
self.elements.append(self.backboard3)
self.elements.append(self.player)
self.elements.append(self.text)
def add_ball(self):
ball = Ball(self.player.x, self.player.y-125)
print("first self.player.y: {}".format(self.player.y))
#if self.status == Status.shoot:
self.elements.append(ball)
def random_ball_drop(self):
# if self.status == Status.shoot:
if self.next_time - time.time() < -2:
# if self.status == Status.shoot:
self.next_time = time.time() + 2
self.add_ball()
print("First time: {}".format(time.time()))
print("first Add.ball: {}".format(self.add_ball()))
#if self.status == Status.shoot:
elif self.next_time - time.time() < 0:
if random.uniform(0, 1) < 0.01:
self.next_time = time.time() + 2
self.add_ball()
print("Second time: {}".format(time.time()))
def check_status(self, element):
if type(element) is Ball:
dist = math.sqrt((element.x - self.backboard1.x) ** 2 + (element.y - self.backboard1.y) ** 2)
if dist < self.backboard1.catch_radius:
self.text.score += 1
return False
elif element.y >= bg_height:
self.text.lives -= 1
print("Text.lives: {}".format(self.text.lives))
return False
return True
def remove_ball(self):
self.elements = [e for e in self.elements if self.check_status(e)]
#print("self.element: {}".format(self.elements))
def update(self):
# if self.status == Status.shoot:
for element in self.elements:
element.update()
#print("first element.update: {}".format(element.update()))
# if self.status == Status.shoot:
self.random_ball_drop()
self.remove_ball()
print("Random_ball_drop from update block: {}".format(self.random_ball_drop()))
print("remove_ball: {}".format(self.remove_ball()))
def change_to_initial_state(self):
self.init_elements()
for e in self.elements:
e.change_to_initial_position()
class GameElement:
def __init__(self, x, y, direction_x, direction_y, speed):
self.initial_x = x
self.initial_y = y
self.x = x
self.y = y
self.direction_x = direction_x
self.direction_y = direction_y
self.speed = speed
def change_to_initial_position(self):
self.x = self.initial_x
self.y = self.initial_y
def update(self):
pass
class Background(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
class Floor(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
class Backboard1(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard1_height / 2) + (ball_height / 2) + 10
class Backboard2(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard2_height / 2) + (ball_height / 2) + 10
class Backboard3(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard3_height / 2) + (ball_height / 2) + 10
class Player(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, speed=6)
self.img_direction = ImgDirection.left
def update(self):
if self.direction_x == HorizontalDirection.left:
if self.x > 0:
self.move()
elif self.direction_x == HorizontalDirection.right:
if self.x < bg_width:
self.move()
def move(self):
self.x += self.direction_x * self.speed
self.direction_x = HorizontalDirection.none
class Ball(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.up, speed=10)
def update(self):
self.y += self.direction_y*self.speed
print("This is self.y: {}".format(self.y))
class TextInfo(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, speed=0)
self.score = 0
self.lives = 3
def change_to_initial_position(self):
self.score = 0
self.lives = 3
super().change_to_initial_position()
controller.py
from model import *
class GameController:
def __init__(self, model):
self.model = model
pass
def start_new_game(self):
self.model.change_to_initial_state()
self.model.status = Status.run
def continue_game(self):
self.model.status = Status.run
print(Status.run)
def exit_game(self):
self.model.status = Status.terminate
def press_p(self, event):
if self.model.status == Status.run:
self.model.status = Status.pause
print(Status.pause)
def press_left(self, event):
self.model.player.direction_x = HorizontalDirection.left
self.model.player.img_direction = ImgDirection.left
print(HorizontalDirection.left)
def press_right(self, event):
self.model.player.direction_x = HorizontalDirection.right
self.model.player.img_direction = ImgDirection.right
print(HorizontalDirection.right)
def press_space(self, event):
if self.model.status == Status.run:
self.model.status = Status.shoot
self.model.update()
print(Status.shoot)
def update_model(self):
if self.model.status == Status.run:
self.model.update()
view.py
import tkinter as tk
from PIL import ImageTk, Image
from model import *
class GameImages:
def __init__(self):
# background
self.bg_pil_img = Image.open('./resources/bg.png')
self.bg_img = ImageTk.PhotoImage(self.bg_pil_img)
# floor
self.floor_pil_img = Image.open('./resources/floor.png')
self.floor_img = ImageTk.PhotoImage(self.floor_pil_img)
# backboard1
self.backboard1_pil_img = Image.open('./resources/backboard1.png')
self.backboard1_pil_img = self.backboard1_pil_img.resize((backboard1_height, backboard1_width))
self.backboard1_img = ImageTk.PhotoImage(self.backboard1_pil_img)
# backboard2
self.backboard2_pil_img = Image.open('./resources/backboard2.png')
self.backboard2_pil_img = self.backboard2_pil_img.resize((backboard2_height, backboard2_width))
self.backboard2_img = ImageTk.PhotoImage(self.backboard2_pil_img)
# backboard3
self.backboard3_pil_img = Image.open('./resources/backboard3.png')
self.backboard3_pil_img = self.backboard1_pil_img.resize((backboard3_height, backboard3_width))
self.backboard3_img = ImageTk.PhotoImage(self.backboard3_pil_img)
# player
self.player_pil_img = Image.open('./resources/player.png')
self.player_pil_img_right = self.player_pil_img.resize((player_height, player_width))
self.player_pil_img_left = self.player_pil_img_right.transpose(Image.FLIP_LEFT_RIGHT)
self.player_img_right = ImageTk.PhotoImage(self.player_pil_img_right)
self.player_img_left = ImageTk.PhotoImage(self.player_pil_img_left)
# ball
self.ball_pil_img = Image.open('./resources/ball.png')
self.ball_pil_img = self.ball_pil_img.resize((ball_height, ball_width))
self.ball_img = ImageTk.PhotoImage(self.ball_pil_img)
def get_image(self, element):
if type(element) is Background:
return self.bg_img
if type(element) is Floor:
return self.floor_img
if type(element) is Backboard1:
return self.backboard1_img
if type(element) is Backboard2:
return self.backboard2_img
if type(element) is Backboard3:
return self.backboard3_img
if type(element) is Player:
if element.img_direction == ImgDirection.left:
return self.player_img_left
else:
return self.player_img_right
if type(element) is Ball:
return self.ball_img
return None
class DisplayGame:
def __init__(self, canvas, _id):
self.canvas = canvas
self.id = _id
def delete_from_screen(self):
self.canvas.delete(self.id)
class DisplayGameImage(DisplayGame):
def __init__(self, canvas, element, img):
super().__init__(canvas, canvas.create_image(element.x, element.y, image=img))
class DisplayGameText(DisplayGame):
def __init__(self, canvas, element):
text = "Score: %d\nLives: %d" % (element.score, element.lives)
super().__init__(canvas, canvas.create_text(element.x, element.y, font='12', text=text))
class DisplayMenu(DisplayGame):
def __init__(self, root, canvas, controller):
menu = tk.Frame(root, bg='grey', width=400, height=40)
menu.pack(fill='x')
new_game = tk.Button(menu, text="New Game", width=15, height=2, font='12', command=controller.start_new_game)
new_game.pack(side="top")
continue_game = tk.Button(menu, text="Continue", width=15, height=2, font='12', command=controller.continue_game)
continue_game.pack(side="top")
exit_game = tk.Button(menu, text="Exit Game", width=15, height=2, font='12', command=controller.exit_game)
exit_game.pack(side="top")
_id = canvas.create_window(bg_width / 2, bg_height / 2, window=menu)
super().__init__(canvas, _id)
class GameView:
def __init__(self, model, controller):
self.model = model
self.controller = controller
# root
self.root = tk.Tk()
self.root.title('Basketball Game')
# load images files
self.images = GameImages()
# canvas
self.canvas = tk.Canvas(self.root, width= bg_width, height= bg_height)
self.canvas.pack()
self.root.update()
# canvas elements id
self.elements_id = []
self.add_elements_to_canvas()
self.add_event_handlers()
self.is_menu_open = False
self.draw()
self.root.mainloop()
def add_elements_to_canvas(self):
for e in self.model.elements:
if type(e) is TextInfo:
self.elements_id.append(DisplayGameText(self.canvas, e))
else:
self.elements_id.append(DisplayGameImage(self.canvas, e, self.images.get_image(e)))
if self.model.status == Status.pause or self.model.status == Status.game_over:
self.elements_id.append(DisplayMenu(self.root, self.canvas, self.controller))
self.is_menu_open = True
def add_event_handlers(self):
self.root.bind("<Left>", self.controller.press_left)
self.root.bind("<Right>", self.controller.press_right)
self.root.bind("p", self.controller.press_p)
self.root.bind("<space>",self.controller.press_space)
def draw(self):
self.controller.update_model()
if self.model.status == Status.run or not self.is_menu_open:
self.is_menu_open = False
self.canvas.delete("all")
self.add_elements_to_canvas()
if self.model.status == Status.terminate:
self.root.destroy()
else:
self.canvas.after(5, self.draw)
In this test script I draw a square I may zoom in by using the mouse wheel.
If I right click inside a cell I get the right cell coordinates (not x and y, but column and row): this is exactly what I expect it to write to the console in the background.
If I, instead, move the canvas by pressing the mouse left button and dragging it somewhere else, the coordinates are not right any more.
Where do I get the delta x and delta y (or offsets) to give back the right info?
FYI:
1) get_pos() is the method that does the check and produces the result.
2) the following code has been tested on Ubuntu 16.10 (with the latest updates) running Python 3.5.2.
import tkinter as tk
import tkinter.ttk as ttk
class GriddedMazeCanvas(tk.Canvas):
def almost_centered(self, cols, rows):
width = int(self['width'])
height = int(self['height'])
cell_dim = self.settings['cell_dim']
rows = rows % height
cols = cols % width
w = cols * cell_dim
h = rows * cell_dim
if self.zoom < 0:
raise ValueError('zoom is negative:', self.zoom)
zoom = self.zoom
if self.drawn() and 1 != zoom:
w *= zoom
h *= zoom
h_shift = (width - w) // 2
v_shift = (height - h) // 2
return [h_shift, v_shift,
h_shift + w, v_shift + h]
def __init__(self, *args, **kwargs):
if 'settings' not in kwargs:
raise ValueError("'settings' not passed.")
settings = kwargs['settings']
del kwargs['settings']
super().__init__(*args, **kwargs)
self.config(highlightthickness=0)
self.settings = settings
self.bind_events()
def draw_maze(self, cols, rows):
self.cols = cols
self.rows = rows
if self.not_drawn():
self.cells = {}
self.cell_dim = self.settings['cell_dim']
self.border_thickness = self.settings['border_thickness']
self.zoom = 1
self.delete(tk.ALL)
maze, coords = self._draw_maze(cols, rows, fix=False)
lines = self._draw_grid(coords)
return maze, lines
def _draw_maze(self, cols, rows, fix=True):
data = self.settings
to_max = data['to_max']
border_thickness = data['border_thickness']
poligon_color = data['poligon_color']
poligon_border_color = data['poligon_border_color']
coords = self.almost_centered(cols, rows)
if fix:
# Fix for the disappearing NW borders
if to_max == cols:
coords[0] += 1
if to_max == rows:
coords[1] += 1
maze = self.create_rectangle(*coords,
fill=poligon_color,
outline=poligon_border_color,
width=border_thickness,
tag='maze')
return maze, coords
def _draw_grid(self, coords):
data = self.settings
poligon_border_color = data['poligon_border_color']
cell_dim = data['cell_dim']
if coords is None:
if self.not_drawn():
raise ValueError('The maze is still uninitialized.')
x1, y1, x2, y2 = self.almost_centered(self.cols, self.rows)
else:
x1, y1, x2, y2 = coords
zoom = self.zoom
if self.drawn() and 1 != zoom:
if self.zoom < 1:
self.zoom = zoom = 1
print('no zooming below 1.')
else:
cell_dim *= zoom
lines = []
for i, x in enumerate(range(x1, x2, cell_dim)):
line = self.create_line(x, y1, x, y2,
fill=poligon_border_color,
tags=('grid', 'grid_hl_{}'.format(i)))
lines.append(line)
for i, y in enumerate(range(y1, y2, cell_dim)):
line = self.create_line(x1, y, x2, y,
fill=poligon_border_color,
tags=('grid', 'grid_vl_{}'.format(i)))
lines.append(line)
return lines
def drawn(self):
return hasattr(self, 'cells')
def not_drawn(self):
return not self.drawn()
def bind_events(self):
self.bind('<Button-4>', self.onZoomIn)
self.bind('<Button-5>', self.onZoomOut)
self.bind('<ButtonPress-1>', self.onScrollStart)
self.bind('<B1-Motion>', self.onScrollMove)
self.tag_bind('maze', '<ButtonPress-3>', self.onMouseRight)
def onScrollStart(self, event):
print(event.x, event.y, self.canvasx(event.x), self.canvasy(event.y))
self.scan_mark(event.x, event.y)
def onMouseRight(self, event):
col, row = self.get_pos(event)
print('zoom:', self.zoom, ' col, row:', col, row)
def onScrollMove(self, event):
delta = event.x, event.y
self.scan_dragto(*delta, gain=1)
def onZoomIn(self, event):
if self.not_drawn():
return
max_zoom = 9
self.zoom += 1
if self.zoom > max_zoom:
print("Can't go beyond", max_zoom)
self.zoom = max_zoom
return
print('Zooming in.', event.num, event.x, event.y, self.zoom)
self.draw_maze(self.cols, self.rows)
def onZoomOut(self, event):
if self.not_drawn():
return
self.zoom -= 1
if self.zoom < 1:
print("Can't go below one.")
self.zoom = 1
return
print('Zooming out.', event.num, event.x, event.y, self.zoom)
self.draw_maze(self.cols, self.rows)
def get_pos(self, event):
x, y = event.x, event.y
cols, rows = self.cols, self.rows
cell_dim, zoom = self.cell_dim, self.zoom
x1, y1, x2, y2 = self.almost_centered(cols, rows)
print('x1, y1, x2, y2:', x1, y1, x2, y2,
' bbox:', self.bbox('maze'))
if not (x1 <= x <= x2 and y1 <= y <= y2):
print('Here we are out of bounds.')
return None, None
scale = zoom * cell_dim
col = (x - x1) // scale
row = (y - y1) // scale
return col, row
class CanvasButton(ttk.Button):
def freeze_origin(self):
if not hasattr(self, 'origin'):
canvas = self.canvas
self.origin = canvas.xview()[0], canvas.yview()[0]
def reset(self):
canvas = self.canvas
x, y = self.origin
canvas.yview_moveto(x)
canvas.xview_moveto(y)
def __init__(self, *args, **kwargs):
if 'canvas' not in kwargs:
raise ValueError("'canvas' not passed.")
canvas = kwargs['canvas']
del kwargs['canvas']
super().__init__(*args, **kwargs)
self.config(command=self.reset)
self.canvas = canvas
root = tk.Tk()
settings = {'cell_dim': 3,
'to_max': 200,
'border_thickness': 1,
'poligon_color': '#F7F37E',
'poligon_border_color': '#AC5D33'}
frame = ttk.Frame(root)
canvas = GriddedMazeCanvas(frame,
settings=settings,
width=640,
height=480)
button = CanvasButton(frame, text='Reset', canvas=canvas)
button.freeze_origin()
canvas.draw_maze(20, 10)
canvas.grid(row=0, column=0, sticky=tk.NSEW)
button.grid(row=1, column=0, sticky=tk.EW)
frame.rowconfigure(0, weight=1)
frame.grid()
root.mainloop()
Looking at a previously answered question, I learned how to find the delta x and delta y I was looking for.
So, the solution is:
def get_pos(self, event):
x, y = event.x, event.y
cols, rows = self.cols, self.rows
cell_dim, zoom = self.cell_dim, self.zoom
x1, y1, x2, y2 = self.almost_centered(cols, rows)
# the following line stores deltax and deltay into x0, y0
x0, y0 = int(self.canvasx(0)), int(self.canvasy(0))
# then it is trivial to compute the solution
xa, ya = x1 - x0, y1 - y0
xb, yb = x2 - x0, y2 - y0
if not (xa <= x <= xb and ya <= y <= yb):
print('Here we are out of bounds.')
return None, None
scale = zoom * cell_dim
col = (x - xa) // scale
row = (y - ya) // scale
return col, row
Why does Tk().after only execute once and how to fix it?
I tried to rerun .after() within tick(), but if I uncomment the line, the windows os not shown at all.
Using Windows 8.1 and Python 3
import tkinter as tk
def tick():
matrix_size = board.get_matrix_size()
alive_neighbours = [[None] * matrix_size for i in range(matrix_size)]
for row in range(matrix_size):
for col in range(matrix_size):
alive_neighbours[row][col] = get_alive_neighbours(row, col)
for row in range(matrix_size):
for col in range(matrix_size):
if alive_neighbours[row][col] == 3:
board.create_cell(row, col)
elif alive_neighbours[row][col] < 2:
board.kill_cell(row, col)
elif alive_neighbours[row][col] == 2 and board.get_cell_state(row, col) == "alive":
continue
elif alive_neighbours[row][col] > 3:
board.kill_cell(row, col)
# root.after(1000, tick())
def get_alive_neighbours(row, col):
alive_neighbour_count = 0
for relative_row in range(-1, 2): # Top cells
for relative_col in range(-1, 2):
if relative_row == 0 and relative_col == 0:
continue # Do not current cell as neighbour
else:
cell_state = board.get_cell_state(row + relative_row, col + relative_col)
if cell_state == "alive":
alive_neighbour_count += 1
return alive_neighbour_count
class GameBoard(tk.Frame):
matrix_size = 10
grid_size = 32
matrix = []
alive_color = "white"
dead_color = "grey"
def __init__(self, master=None):
self.matrix = [[None] * self.matrix_size for i in range(self.matrix_size)]
canvas_width = self.matrix_size * self.grid_size
canvas_height = self.matrix_size * self.grid_size
tk.Frame.__init__(self, master)
self.canvas = tk.Canvas(self, width=canvas_width, height=canvas_height)
self.draw_board()
self.initialize_cells()
self.canvas.grid()
def draw_board(self):
color = "white"
for row in range(len(self.matrix)):
for col in range(len(self.matrix[row])):
# color = "white" if (col+row)%2==0 else "lightgrey"
color = "lightgrey"
x0 = (col * self.grid_size + 2) # Why +2?
y0 = (row * self.grid_size + 2) # Why +2
x1 = x0 + self.grid_size
y1 = y0 + self.grid_size
id = self.canvas.create_rectangle(x0, y0, x1, y1, fill=color, width=0)
self.matrix[row][col] = id
def create_cell(self, row, column):
grid_item = self.matrix[row][column]
self.canvas.itemconfigure(grid_item, fill=self.alive_color)
def kill_cell(self, row, column):
grid_item = self.matrix[row][column]
self.canvas.itemconfigure(grid_item, fill=self.dead_color)
def get_cell_state(self, row, column):
try:
grid_item = self.matrix[row][column]
except IndexError:
return "dead"
else:
fill_color = self.canvas.itemcget(grid_item, "fill")
if fill_color == "white":
return "alive"
else:
return "dead"
def get_matrix(self):
return self.matrix
def get_matrix_size(self):
return self.matrix_size
def initialize_cells(self):
self.create_cell(1, 1)
self.create_cell(1, 2)
self.create_cell(2, 1)
self.grid()
if __name__ == "__main__":
root = tk.Tk()
root.resizable(0, 0)
board = GameBoard(master=root)
root.after(1000, tick())
root.mainloop()
The problem is that you're calling the methods when using them as a parameter, thus using the return value of the tick function (None).
Uncomment the line you mentioned and replace your after calls with
root.after(1000, tick)
Therefore, you're passing the tick function itself.
I used OpenGL to draw about 20 circles. Each circle has 2 lines, ~10 segments, and all of them have different colors and lenght. FPS ~=4. How can I do this faster?
I am using Python 3 on Ubuntu
Code:
class ScreenOpenGL(Screen):
def __init__(self,layers,layers_lock):
""" Инициализирует экран и запускает его, потом всю программу """
Screen.__init__(self,"OpenGL",layers,layers_lock)
self.window = 0
self.quad = None
self.keypress = []
print("Fuck")
# self.infoScreen = ScreenCursesInfo()
self.infoScreen = ScreenStandartInfo()
GLUT.glutInit(sys.argv)
GLUT.glutInitDisplayMode(GLUT.GLUT_RGBA | GLUT.GLUT_DOUBLE | GLUT.GLUT_ALPHA | GLUT.GLUT_DEPTH)
GLUT.glutInitWindowSize(640, 480)
GLUT.glutInitWindowPosition(400, 400)
self.window = GLUT.glutCreateWindow(b"Project Evoluo alpha")
GLUT.glutDisplayFunc(self._loop) # Функция, отвечающая за рисование
GLUT.glutIdleFunc(self._loop) # При простое перерисовывать
GLUT.glutReshapeFunc(self._resize) # изменяет размеры окна
GLUT.glutKeyboardFunc(self._keyPressed) # Обрабатывает нажатия
self._initGL(640, 480)
field_params(640, 480)
print("Fuck")
def run(self):
# для threading
GLUT.glutMainLoop()
def _initGL(self,Width,Height):
GL.glShadeModel(GL.GL_SMOOTH);
GL.glClearColor(0.0, 0.0, 0.0, 0.0) # This Will Clear The Background Color To Black
GL.glClearDepth(1.0) # Enables Clearing Of The Depth Buffer
GL.glDepthFunc(GL.GL_LESS) # The Type Of Depth Test To Do
GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST)
GL.glEnable(GL.GL_BLEND); # Enable Blending
GL.glLineWidth(1.);
GL.glDisable(GL.GL_LINE_SMOOTH)
GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
self.width = Width
self.height = Height
self.quad = GLU.gluNewQuadric()
GLU.gluQuadricNormals(self.quad, GLU.GLU_SMOOTH)
GLU.gluQuadricTexture(self.quad, GL.GL_TRUE)
def _resize(self,Width,Height):
if Height == 0:
Height = 1
self.width = Width
self.height = Height
field_params(Width,Height)
GL.glViewport(0, 0, Width, Height) # Reset The Current Viewport And Perspective Transformation
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
GL.glOrtho(0.0,Width,0.0,Height,-1.0,1.0)
GL.glMatrixMode(GL.GL_MODELVIEW) # Select The Modelview Matrix
GL.glLoadIdentity()
def update(self):
GLUT.glutSwapBuffers()
def clear(self):
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
GL.glLoadIdentity() # Reset The View
def write(self,pos,str):
pass
def __del__(self):
del(self.infoScreen)
GLUT.glutDestroyWindow(self.window)
self._end()
# sys.exit()
def getch(self):
if self.keypress != []:
# print
return self.keypress.pop(-1)[0]
else:
return None
def _keyPressed(self,*args):
if args != None:
self.keypress.append(args)
print(self.keypress)
def _draw_prepare(self):
del(self._layers)
self._layers = []
for layer in layers:
if layer.__class__ == LayerObjects:
self._layers.append([layer.type,copy.deepcopy(layer.get_objs()),copy.copy(layer._attacked)])
def draw_eyes(self,vis,r_max,dphi):
for x in range(0,7):
GLU.gluPartialDisk(self.quad,
1,
vis[x][0] * r_max,
5,
3,
- ((x - 3) * 15 + 7.5 + dphi / pi * 180) + 90,
15)
def draw_sensitivity(self,sens,r_max,dphi):
for x in range(0,5):
GLU.gluPartialDisk(self.quad,
1,
sens[x] * r_max,
5,
3,
- (52.5 + (x+1) * 51 + dphi / pi * 180) + 90,
51)
pass
def draw_obj(self,obj,_id,pos,circle,lines,attacked):
# Кружок
GL.glLoadIdentity()
GL.glTranslatef(pos[0]-1,pos[1]-1,0)
red,green,blue = obj.color # берём цвет
GL.glColor3f(red,green,blue)
GLU.gluDisk(self.quad,*circle)
#Глазки
GL.glColor3f(1-red,1-green,1-blue)
try:
eyes = obj.eyes
except NameError:
pass
else:
self.draw_eyes(obj.eyes.eyes,obj.radius * k_screen,obj.pos[1])
# Прикосновения
GL.glColor3f(1,0,0)
try:
sensitivity = obj.sensitivity
except NameError:
pass
else:
self.draw_sensitivity(obj.sensitivity._sens,obj.radius * k_screen,obj.pos[1])
# Полосочки
GL.glBegin(GL.GL_LINES)
for color,x,y in lines:
GL.glColor3f(*color)
GL.glVertex3f(0,0,0)
GL.glVertex3f(x,y,0)
GL.glEnd()
def draw(self,layer):
global width,height
if layer[0] == "Layer Objects":
attacked = layer[2]
for obj in layer[1]:
#Стрелочки-направления
pos = [int(x) for x in obj.get_pos_screen()]
positions = [pos,]
radius_scr = obj.radius * k_screen
att = Vector(radius_scr * (1 +obj._attack_range*obj._attack),
obj.pos[1],
isPolar = True
)
speed = obj.speed[0] * k_screen * 5
if pos[0] < radius_scr:
positions.append([pos[0] + self.width,pos[1]])
if pos[0] + radius_scr > self.width:
positions.append([pos[0] - self.width,pos[1]])
if pos[1] < radius_scr:
positions.append([pos[0],pos[1] + self.height])
if pos[1] + radius_scr > self.height:
positions.append([pos[0],pos[1] - self.height])
for ps in positions:
self.draw_obj(obj,obj._id, ps , [0,obj.radius*k_screen,20,1], [ [ (1,0,0) , att.x, att.y ], [ (0,0,1) , speed.x, speed.y] ] , attacked)
self.infoScreen.draw()
Function code, that draws:
def _draw_prepare(self):
""" Копирует из глобальной переменной всё, что ей нужно """
self._layers = copy.deepcopy(layers)
def _loop(self):
global tick
if (self.ch == b'q') or (self.ch == b'\xb1') or (self.ch == 27) or (self.ch == 113):
isEnd = True
self.__del__()
del(self)
return 0
elif (self.ch == b's'):
self._is_draw = not self._is_draw
print("changed to %d" %self._is_draw)
else:
if (self.last_tick != tick) and (self._is_draw):
self.layers_lock.acquire(1)
self._draw_prepare()
self.layers_lock.release()
self.last_tick = tick
# рисует сцену
return self._main()
def _main(self):
global tick
self.clear()
if self._is_draw:
for layer in self._layers:
self.draw(layer)
self.update()
self.ch = self.getch()
And on GitHub
Hey I am creating a roguelike game with python using Libtcod. I have code where the game makes random rooms. It should be making multiple rooms until one crosses and then stops making the rooms. The problem is it always only creates 1 room. Here is the hole code:
import libtcodpy as libtcod;
SCREEN_WIDTH = 80;
SCREEN_HEIGHT = 50;
MAP_WIDTH = 80
MAP_HEIGHT = 45
ROOM_MAX_SIZE = 10
ROOM_MIN_SIZE = 6
MAX_ROOMS = 30
LIMIT_FPS = 20;
color_dark_wall = libtcod.Color(0, 0, 100)
color_dark_ground = libtcod.Color(50, 50, 150)
class Tile:
def __init__(self, blocked, block_sight = None):
self.blocked = blocked
if block_sight is None: block_sight = blocked
self.block_sight = block_sight
class Rect:
def __init__(self, x, y , w, h):
self.x1 = x
self.y1 = y
self.x2 = x + w
self.y2 = y + h
def center(self):
center_x = (self.x1 + self.x2) / 2
center_y = (self.y1 + self.y2) / 2
return (center_x, center_y)
def intersect(self, other):
return (self.x1 <= other.x2 and self.x2 >= other.x1 and
self.y1 <= other.y2 and self.y2 >= other.y1)
class Object:
def __init__(self, x, y, char, color):
self.x = x
self.y = y
self.char = char
self.color = color
def move(self, dx, dy):
if not map[self.x + dx][self.y + dy].blocked:
self.x += dx
self.y += dy
def draw(self):
libtcod.console_set_default_foreground(con, self.color)
libtcod.console_put_char(con, self.x, self.y, self.char, libtcod.BKGND_NONE)
def clear(self):
libtcod.console_put_char(con, self.x, self.y, ' ', libtcod.BKGND_NONE)
def create_room(room):
global map
for x in range(room.x1 + 1, room.x2):
for y in range(room.y1 + 1, room.y2):
map[x][y].blocked = False
map[x][y].block_sight = False
def create_h_tunnel(x1, x2, y):
global map
for x in range(min(x1, x2), max(x1, x2) + 1):
map[x][y].blocked = False
map[x][y].block_sight = False
def create_v_tunnel(y1, y2, x):
global map
print("Hello")
for y in range(min(y1, y2), max(y1, y2) + 1):
map[x][y].blocked = False
map[x][y].block_sight = False
def make_map():
global map
map = [[ Tile(True)
for y in range(MAP_HEIGHT) ]
for x in range(MAP_WIDTH) ]
rooms = []
num_rooms = 0
for r in range(MAX_ROOMS):
w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
x = libtcod.random_get_int(0, 0, MAP_WIDTH - w - 1)
y = libtcod.random_get_int(0, 0, MAP_HEIGHT - h -1)
new_room = Rect(x, y, w, h)
failed = False
for other_room in rooms:
if new_room.intersect(other_room):
failed = True
break
if not failed:
create_room(new_room)
(new_x, new_y) = new_room.center()
if num_rooms == 0:
player.x = new_x
player.y = new_y
else:
(prev_x, prev_y) = rooms[num_rooms-1].center()
if libtcod.random_get_int(0, 0, 1) == 1:
create_h_tunnel(prev_x, new_x, prev_y)
create_v_tunnel(prev_y, new_y, new_x)
else:
create_v_tunnel(prev_y, new_y, prev_x)
create_h_tunnel(prev_x, new_x, new_y)
rooms.append(new_room)
num_rooms += 1
def render_all():
global color_dark_wall, color_light_wall
global color_dark_ground, color_light_ground
for y in range(MAP_HEIGHT):
for x in range (MAP_WIDTH):
wall = map[x][y].block_sight
if wall:
libtcod.console_set_char_background(con, x, y, color_dark_wall, libtcod.BKGND_SET)
else:
libtcod.console_set_char_background(con, x, y, color_dark_ground, libtcod.BKGND_SET)
for object in objects:
object.draw()
libtcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
def handle_keys():
key = libtcod.console_check_for_keypress()
if key.vk == libtcod.KEY_ENTER and key.lalt:
libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
elif key.vk == libtcod.KEY_ESCAPE:
return True #exit game
if libtcod.console_is_key_pressed(libtcod.KEY_UP):
player.move(0, -1)
elif libtcod.console_is_key_pressed(libtcod.KEY_DOWN):
player.move(0, 1)
elif libtcod.console_is_key_pressed(libtcod.KEY_LEFT):
player.move(-1, 0)
elif libtcod.console_is_key_pressed(libtcod.KEY_RIGHT):
player.move(1, 0)
libtcod.console_set_custom_font('terminal10x10_gs_tc.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD);
libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'Lets Crawl', False);
libtcod.sys_set_fps(LIMIT_FPS);
con = libtcod.console_new(SCREEN_WIDTH, SCREEN_HEIGHT)
player = Object(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, '#', libtcod.green)
npc = Object(SCREEN_WIDTH/2 - 5, SCREEN_HEIGHT/2, '#', libtcod.yellow)
objects = [npc, player]
#makes the map
make_map()
while not libtcod.console_is_window_closed():
render_all()
#libtcod.console_check_for_keypress()
libtcod.console_flush();
for object in objects:
object.clear()
exit = handle_keys()
if exit:
break
And here is the make_map() function where I suspect the problem is:
def make_map():
global map
map = [[ Tile(True)
for y in range(MAP_HEIGHT) ]
for x in range(MAP_WIDTH) ]
rooms = []
num_rooms = 0
for r in range(MAX_ROOMS):
w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
x = libtcod.random_get_int(0, 0, MAP_WIDTH - w - 1)
y = libtcod.random_get_int(0, 0, MAP_HEIGHT - h -1)
new_room = Rect(x, y, w, h)
failed = False
for other_room in rooms:
if new_room.intersect(other_room):
failed = True
break
if not failed:
create_room(new_room)
(new_x, new_y) = new_room.center()
if num_rooms == 0:
player.x = new_x
player.y = new_y
else:
(prev_x, prev_y) = rooms[num_rooms-1].center()
if libtcod.random_get_int(0, 0, 1) == 1:
create_h_tunnel(prev_x, new_x, prev_y)
create_v_tunnel(prev_y, new_y, new_x)
else:
create_v_tunnel(prev_y, new_y, prev_x)
create_h_tunnel(prev_x, new_x, new_y)
rooms.append(new_room)
num_rooms += 1
After the program creates the room, it adds the room to the list but does it not continue to make rooms? Or is it that each room created is the same size (even though im passing random numbers) and it creates the first room and then stops? I don't understand...
I suspect you have an indentation error. Looks like everything after the definition of w, h, x and y is supposed to be part of the same for loop, so should be indented to the same level.