I've coded a GUI based Othello game. Am having a bit trouble re-sizing the game board. here is the code I have so far:
BOXWIDTH=60
BOXHEIGHT=60
class player:
"""Make a user player to play the game via a GUI."""
def __init__(self,row,column):
# create the GUI state variables
self.alive = True
self.move = None
self.move_played = False
# create the GUI windows and handlers
self.root = tkinter.Tk()
self.root.wm_title('Othello')
self.root.protocol("WM_DELETE_WINDOW", self.quit)
# create a button to get the No move command from the user
tkinter.Button(self.root, text="No Move", command = self.nomove).pack()
# create a label for displaying the next player's name
self.movemesg = tkinter.StringVar()
tkinter.Label(self.root, textvariable=self.movemesg).pack()
self.canvas = tkinter.Canvas(self.root, bg="darkgreen",
height = BOXHEIGHT*row,
width = BOXWIDTH*column)
self.canvas.bind("<Button-1>", self.click)
# create a box for highlighting the last move
self.lastbox = self.canvas.create_rectangle(0, 0, BOXWIDTH*column,
BOXHEIGHT*row,
outline="red")
# draw the game canvas
for i in range(1,row):
# horizontal lines
self.canvas.create_line(0, i*BOXHEIGHT,
BOXWIDTH*column, i*BOXHEIGHT)
for i in range(1,column):
# vertical lines
self.canvas.create_line(i*BOXWIDTH, 0,
i*BOXWIDTH, BOXHEIGHT*row)
# the board will store the widgets to be displayed in each square
self.board = [[None for y in range(row)]
for x in range(column)]
# display the window
self.canvas.pack()
self.canvas.focus_set()
self.root.update()
def draw_board(self, game, last_move):
"""Draw an othello game on the board."""
ws = str(sum(x.count(1) for x in game.board))
bs = str(sum(x.count(-1) for x in game.board))
if game.player == -1:
self.movemesg.set("Black to play "+'\nSCORE: White = '+ws+' Black = ' + bs)
else:
self.movemesg.set("White to play "+'\nSCORE: White = '+ws+' Black = ' + bs)
for i in range(game.column):
for j in range(game.row):
color = game.get_color((i,j))
if color == -1:
board_color = "black"
elif color == 1:
board_color = "white"
else:
if self.board[i][j] is not None:
self.canvas.delete(self.board[i][j])
self.board[i][j] = None
continue
if self.board[i][j] is None:
self.board[i][j] = self.canvas.create_oval(
i*BOXWIDTH+2, j*BOXHEIGHT+2, (i+1)*BOXWIDTH-2,
(j+1)*BOXHEIGHT-2, fill = board_color)
else:
self.canvas.itemconfig(self.board[i][j], fill=board_color)
I want the game board to resize i.e it must cause the area in which the board is drawn to change size correspondingly, with the game board redrawn to fill the available space. Any help on this would be great.
Your first problem is that the canvas wasn't told to grow when the main window grows. Change your pack statement to this:
self.canvas.pack(fill="both", expand=True)
Next, you will need to create a binding that gets called whenever the canvas resizes. You can do this by binding to <Configure>. For example:
self.canvas.bind("<Configure>", self.on_configure)
Finally, you will need to create a function that does nothing except redraw the board. You will call this method from the on_configure method.
Related
I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()
When using this code I get an error.
While as far as I can tell the surface does have all the positional arguments filled.
I create the player from the block class and I pass in the arguments to create the player. (color, width and height)
After creating the player I add it to all_sprites list and use player.rect.x and player.rect.y to position the block on the screen.
In the end I just draw the all_sprites_list to screen.
However when doing this I get this error:
TypeError: draw() missing 1 required positional argument: 'surface'
Could someone tell me what I am doing wrong here?
class Block(pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.width = width
screen_width = 700
screen_height = 500
size = (screen_width, screen_height)
screen = pygame.display.set_mode(size)
# Sprites lists
all_sprites_list = pygame.sprite.Group
# Create the player
block_width = 30
block_height = 15
player = Block(BLUE, block_width, block_height)
# Set the initial position for the player in the center of the screen
player.rect.x = screen_width/2 - block_width/2
player.rect.y = screen_height/2 - block_height/2
# Add the player to all_sprites_list
all_sprites_list.add(player)
# -------- Main Program Loop -----------
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
all_sprites_list.draw(screen)
pygame.display.flip()
pygame.sprite.Group is a class, and according to the documents located here.
Group(*sprites) -> Group
What you need to do add parentheses where you call pygame.sprite.Group and add all the sprites in that group within the parentheses like this:
all_sprites_list = pygame.sprite.Group(player,enemy,etc...)
At the very least you need to add parentheses after the Group so that the interpreter doesn't treat it as an object, and calls the function properly.
Ok guys.
I am trying to generate 10 balls of random color in Tkinter canvas when I click the generate button.
Program works, and random color choice works for the ball, but I only get one ball generated at a time.
Every time I click the button it randomly moves the ball around, but all I want is 10 balls in 10 random positions at a time. I am using Python 3.4 on a Linux box.
This is a code I've got:
from tkinter import *
import random # to generate random balls
colors = ["red", "blue", "purple", "green", "violet", "black"]
class RandomBalls:
"""
Boilerplate code for window in Tkinter
window = Tk()
window.title("Random title")
window.mainloop()
"""
def __init__(self):
"""
Initialize the window and add two frames, one with button, and another one with
canvas
:return:
"""
window = Tk()
window.title("Random balls")
# A canvas frame
frame1 = Frame(window)
frame1.pack()
self.canvas = Canvas(frame1, width = 200, height = 300, bg = "white")
self.canvas.pack()
# A button frame
frame2 = Frame(window)
frame2.pack()
displayBtn = Button(frame2, text = "Display", command = self.display)
displayBtn.pack()
window.mainloop()
def display(self):
for i in range(0, 10):
self.canvas.delete("circle") # delete references to the old circle
self.x1 = random.randrange(150)
self.y1 = random.randrange(200)
self.x2 = self.x1 + 5
self.y2 = self.y1 + 5
self.coords = self.x1, self.y1, self.x2, self.y2
self.canvas.create_oval(self.coords, fill = random.choice(colors), tags = "circle")
self.canvas.update()
RandomBalls()
Every time through your loop you are deleting everything you created before, including what you created the previous iteration. Move the delete statement outside of the loop:
def display(self):
self.canvas.delete("circle")
for i in range(0, 10):
...
So Ive been having issues with getting a sprite to stay withing the bounds of the screen. I got it to work with a simple rect(0,0,16,16), but i cant seem to get it to work with a sprite being blit onto the screen. What do i need to change in order to keep my sprite clamped within the screen res? I only just started today using classes to orgonize code so any input is appreciated and helpful.
import pygame
from pygame.locals import *
from pygame import Color
class Game():
""" Lets try to get this going by simple steps
One by one. First step, lets figure how to make a class
that can do the display stuff. Lord have mercy on my soul"""
def __init__(self, wi=256, hi=224, multii=3):
"""Initialization"""
pygame.init()
self.runGame = True
self.width = wi*multii
self.height = hi*multii
self.spritesize = 16*multii
self.clock = pygame.time.Clock()
self.fps = self.clock.get_fps()
self.screen = pygame.display.set_mode((self.width, self.height))
self.kl = []
self.walk = [0, 0]
self.speed = multii*1.5
self.x,self.y = self.width/2, self.height/2
self.playerSpr = pygame.image.load('images/'+'link1.png').convert_alpha()
self.playerRec = Rect(self.playerSpr.get_rect())
def mainLoop(self):
"""Loop through the main game routines
1. Drawing 2. Input handling 3. Updating
Then loop through it until user quits"""
while self.runGame:
self.clock.tick(60)
self.events()
self.draw()
def events(self):
"""Time to handle some events"""
for e in pygame.event.get():
if (e.type == pygame.QUIT) or (e.type == KEYDOWN and e.key == K_ESCAPE):
self.runGame = False
break
if e.type==KEYDOWN:
if e.key==pygame.K_a: self.kl.append(1)
if e.key==pygame.K_d: self.kl.append(2)
if e.key==pygame.K_w: self.kl.append(3)
if e.key==pygame.K_s: self.kl.append(4)
if e.type==pygame.KEYUP:
if e.key==pygame.K_a: self.kl.remove(1)
if e.key==pygame.K_d: self.kl.remove(2)
if e.key==pygame.K_w: self.kl.remove(3)
if e.key==pygame.K_s: self.kl.remove(4)
if self.kl[-1:]==[1]: self.walk=[-self.speed, 0]
elif self.kl[-1:]==[2]: self.walk=[ self.speed, 0]
elif self.kl[-1:]==[3]: self.walk=[0,-self.speed]
elif self.kl[-1:]==[4]: self.walk=[0, self.speed]
else: self.walk=[0, 0]
self.x+=self.walk[0]
self.y+=self.walk[1]
def draw(self):
"""Draw and update the main screen"""
self.fps = self.clock.get_fps()
self.screen.fill(Color('purple'))
#print self.screen.get_rect()
#print player_rect
self.playerSpr.clamp_ip(self.screen.get_rect())
#pygame.draw.rect(self.screen, (255, 255, 255), self.playerrect)
self.screen.blit(self.playerSpr, (self.x,self.y), self.playerRec)
pygame.display.set_caption('Grid2. FPS: '+str(self.fps))
pygame.display.update()
game = Game()
game.mainLoop()
Why not use playerRec to keep track of the position of your player instead of the additional x and y attributes?
I suggest also using the move method (or move_ip):
def events(self):
for e in pygame.event.get():
...
self.playerRec.move_ip(*self.walk) # instead of self.x+=self.walk[0] / self.y+=self.walk[1]
def draw(self):
...
# probably do this right after 'move_ip'
self.playerRec.clamp_ip(self.screen.get_rect())
# note that 'blit' accepts a 'Rect' as second parameter
self.screen.blit(self.playerSpr, self.playerRec)
as a side note: You should consider using a Sprite, since it basically combines an Image and a Rect.
Two things:
You are not stopping movement of the sprite when it comes off the screen.
Make an move functions that will get a direction and will decide if it can move more to the side. That way when the right side of the sprite will be of screen, you will not move more to the right.
Since you put your direction keys in a list that works like a stack, you are only getting 1 direction per keypress. If you also want to move diagonally either make two lists one for both directions or use a easier method such as this:
if KEYDOWN == K_LEFT: direction_x = -1
if KEYUP == K_LEFT AND direction_x == -1: direction_x = 0
do this for every key.
Hello, I am new to Python and graphics programming in general. At present I am messing around with creating grids as practice, and I am having problems with getting pygame to put objects on top of the surface window.
Below is my code with comments. I reckon that the problem may be to do with the blit function but I am unsure. Any help would be appreciated. Also, the Python shell does not highlight any exceptions or errors, but the program does not run.
import sys, random, pygame
from pygame.locals import *
def main():
#settings
boardDims = (20,20) #number of cells on board
#pygame settings
cellDims = (20,20) #number of pixels on cells
framerate = 50
colours = {0:(0,0,0), 1:(255,255,255)}
#pygame
pygame.init()
#sets x and y pixels for the window
dims = (boardDims[0] * cellDims[0],
boardDims[1] * cellDims[1])
#sets window, background, and framrate
screen = pygame.display.set_mode(dims)
background = screen.convert()
clock = pygame.time.Clock()
#create new board
board = {}
#iterates over x and y axis of the window
for x in range(boardDims[0]):
for y in range(boardDims[1]):
board[(x,y)] = 0 #sets whole board to value 0
board[(1,1)] = 1 #sets one square at co-ordinates x=1,y=1 to cell
# value 1
return board
running = 1
while running:
# 1 PYGAME
#get input
for event in pygame.event.get():
if event.type == QUIT or \
(event.type == KEYDOWN and event.key == K_ESCAPE):
running = 0
return
#link pygames clock to set framerate
clock.tick(framerate)
for cell in board:
#adds cells dimensions and co-ordinates to object rectangle
rectangle = (cell[0]*cellDims[0], cell[1]* cellDims[1],
cellDims[0], cellDims[1])
#pygame draws the rectabgle on the background using the relevant
#colour and dimensions and co-ordinates outlined in 'rectangle'
square = pygame.draw.rect(background, colours[board[cell]],
rectangle)
#blits and displays object on the background
background.blit(square)
screen.blit(background, (0,0))
pygame.display.flip()
if __name__ == "__main__":
main()
The "return board" statement is exiting your program before any blitting is done.
Also, you can blit the squares directly on the screen variable, ie: you don't need the background variable.