py2app
py2app copied to clipboard
Service exited with abnormal code
I am using py2app 0.21 with python 3.7.7 on macOS Catalina version 10.15.4
When I use -A the app produced will run successfully , but when I exit I get an error and my system log shows the following
com.apple.xpc.launchd[1] (org.pythonmac.unspecified.Othello.6516[41752]): Service exited with abnormal code: 255
If I don't use -A the app produced doesn't run at all, I get an error when I launch it & my system log shows the following (the same as above but with different numbers)
com.apple.xpc.launchd[1] (org.pythonmac.unspecified.Othello.6528[41978]): Service exited with abnormal code: 255
My setup file -
"""
This is a setup.py script generated by py2applet
Usage:
python setup.py py2app
"""
from setuptools import setup
APP = ['Othello.py']
DATA_FILES = ["othello.png"]
OPTIONS = {
'argv_emulation': True,
'iconfile': 'othello.icns',
'packages': ['pygame','random']
}
setup(
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)
My script -
import pygame, random
from pygame.locals import *
pygame.init()
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BRIGHT_RED = (200, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PALE_GREY = (230, 230, 230)
DARK_GREY = ( 75, 75, 75)
icon = pygame.image.load("othello.png")
pygame.display.set_icon(icon)
clock = pygame.time.Clock()
fps = 60
width = 8
height = 8
margin = 80
box_size = 100
screen_size = ((2 * margin) + (width * box_size))
radius = 40
window = pygame.display.set_mode((screen_size, screen_size))
pygame.display.set_caption ('Othello')
def player_black():
player_tile = 'X'
computer_tile = 'O'
game_loop(player_tile, computer_tile)
def player_white():
player_tile = 'O'
computer_tile = 'X'
game_loop(player_tile, computer_tile)
def draw_text(message, size, align, x, y):
basicFont = pygame.font.SysFont('comicsans', size)
text = basicFont.render(message, True, BLACK,)
textRect = text.get_rect()
if align == 'centre':
textRect.centerx = x
elif align == 'left':
textRect.left = x
elif align == 'right':
textRect.right = x
textRect.centery = y
window.blit(text, textRect)
def draw_board(board):
window.fill(GREEN)
for x in range(width):
for y in range(height):
pygame.draw.rect(window, BLACK, (margin +( x * box_size), margin + ( y * box_size), box_size, box_size), 2)
if board[x][y] == 'X':
draw_counter(BLACK, x, y)
elif board[x][y] == 'O':
draw_counter(WHITE, x, y)
def draw_counter(color, x, y):
box_x = (x * box_size) + margin + int(0.5 * box_size)
box_y = (y * box_size) + margin + int(0.5 * box_size)
pygame.draw.circle(window, color, (box_x, box_y), radius)
# convert board coordinates to pixel coordinates
def left_top_coords_of_box(box_x, box_y):
left = (box_x * box_size) + margin
top = (box_y * box_size) + margin
return (left, top)
# Convert from pixel coords (where mouse clicks) to box coords (which box mouse is over)
def get_box_at_pixel(x, y):
for box_x in range(screen_size):
for box_y in range(screen_size): # for each box
left, top = left_top_coords_of_box(box_x, box_y) # pixel coordinates of top left of box
box_rect = pygame.Rect(left, top, box_size, box_size) # full square of box
if box_rect.collidepoint(x, y): # if 'collision' between square of box & point (x, y) is True
return (box_x, box_y) # return box coords
return (None, None)
def get_new_board():
# Create a new, completely blank board
board = []
for i in range(width):
board.append([' ',' ',' ',' ',' ',' ',' ',' '])
return board
def is_valid_move(board, tile, xstart, ystart):
# Calculate if move (xstart, ystart) is valid
# If space already taken or not on board return false
if is_on_board(xstart,ystart) == False or board[xstart][ystart] != ' ':
return False
if tile == 'X':
other_tile ='O'
else:
other_tile = 'X'
tiles_to_flip = []
for xdir, ydir in [[0,1],[1,1],[1,0],[1,-1],[0,-1],[-1,-1],[-1,0],[-1,1]]:
x, y = xstart, ystart # xstart & ystart will remain the same so the computer
x += xdir
y += ydir
while is_on_board(x,y) and board[x][y] == other_tile:
# Keep moving in this direction
x += xdir
y += ydir
if is_on_board(x,y) and board[x][y] == tile:
# There are pieces to flip, go in reverse direction until we reach the original space, noting all tiles
while True:
x -= xdir
y -= ydir
if x == xstart and y == ystart:
break
tiles_to_flip.append([x,y])
# If there are no tiles to flip its not a valid move
if len(tiles_to_flip) == 0:
return False
return tiles_to_flip
def is_on_board(x,y):
# Return True if co-ords are on the board
return x >= 0 and x <= width-1 and y >= 0 and y <= height-1
def get_board_with_valid_moves(board, tile):
# Return a new board with . marking the valid moves the player
board_copy = get_board_copy(board)
for x, y in get_valid_moves(board_copy, tile):
board_copy[x][y] = '.'
return board_copy
def get_valid_moves(board, tile):
# Return a list of [x,y] lsts of valid moves for the given player on the given board
valid_moves = []
for x in range(width):
for y in range(height):
if is_valid_move(board, tile, x, y) != False:
valid_moves.append([x,y])
return valid_moves
def get_score_of_board(board):
# Get score by counting tiles. returns a dictionary with keys 'X' & 'O'
xscore = 0
oscore = 0
for x in range (width):
for y in range(height):
if board[x][y] == 'X':
xscore += 1
if board[x][y] =='O':
oscore += 1
return {'X': xscore, 'O': oscore}
def who_goes_first():
# Randomly chooses who goes first
if random.randint(0,1) == 0:
return 'computer'
else:
return 'player'
def make_move(board, tile, xstart, ystart):
# Place the tile on the board at xstart, ystart & flip any of the opponents pieces
# Check if move is valid
tiles_to_flip = is_valid_move(board, tile, xstart, ystart)
if tiles_to_flip == False:
return False
board[xstart][ystart] = tile
for x, y in tiles_to_flip:
board[x][y] = tile
return True
def get_board_copy(board):
# make a duplicate of the board list & return it
board_copy = get_new_board()
for x in range(width):
for y in range(height):
board_copy[x][y] = board[x][y]
return board_copy
def is_on_corner(x, y):
return(x == 0 or x == width-1) and (y == 0 or y == height-1)
def get_player_move(board, player_tile):
# Ask the player to enter their move
while True:
mouse_x = 0
mouse_y = 0
mouse_clicked = False
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
quit()
elif event.type == MOUSEMOTION:
mouse_x, mouse_y = event.pos
elif event.type == MOUSEBUTTONUP:
mouse_x, mouse_y = event.pos
mouse_clicked = True
box_x, box_y = get_box_at_pixel (mouse_x, mouse_y)
if box_x != None and box_y != None: # the mouse is currently over a box
if is_valid_move(board, player_tile, box_x, box_y) == False:
continue
elif mouse_clicked == False:
# Highlight box
continue
else:
x = box_x
y = box_y
break
return [x, y]
def get_computer_move(board, computer_tile):
while True:
# get all possible moves & randomly shuffle the order
possible_moves = get_valid_moves(board, computer_tile)
random.shuffle(possible_moves)
# always go for a corner if available
for x, y in possible_moves:
if is_on_corner(x, y):
return (x, y)
# Find the highest scoring possible move.
best_score = -1
for x, y in possible_moves:
board_copy = get_board_copy(board)
make_move(board_copy, computer_tile, x, y)
score = get_score_of_board(board_copy)[computer_tile]
if score > best_score:
best_move = [x, y]
best_score = score
return best_move
def print_score(board, player_tile, computer_tile):
# Print the scores
scores = get_score_of_board(board)
player_score = 'Player : ' + str(scores[player_tile])
computer_score = 'Computer : ' + str(scores[computer_tile])
draw_text(player_score, 30, 'left', margin, (margin/2))
draw_text(computer_score, 30, 'right',(screen_size - margin), (margin/2))
def button(msg, x, y, w, h, ic, ac, tc, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w/2 > mouse[0] > x-w/2 and y+h/2 > mouse[1] > y-h/2:
pygame.draw.rect(window, ac, ((x-w/2),(y-h/2), w, h))
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(window, ic, ((x-w/2),(y-h/2), w, h))
basicFont = pygame.font.SysFont('comicsans', 40)
text = basicFont.render(msg, True, tc,)
textRect = text.get_rect()
textRect.center = (x, y)
window.blit(text, textRect)
pygame.display.update()
#Game Loop
def play_game(player_tile, computer_tile):
global turn
# clear the board & place the starting pieces
board = get_new_board()
board[3][3] = 'X'
board[3][4] = 'O'
board [4][3] = 'O'
board[4][4] = 'X'
draw_board(board)
pygame.display.update()
clock.tick(fps)
while True:
# Check for a stalemate
player_valid_moves = get_valid_moves(board, player_tile)
computer_valid_moves = get_valid_moves(board, computer_tile)
# If no-one can move end the game
if player_valid_moves == [] and computer_valid_moves == []:
return board
elif turn == 'player':
if player_valid_moves != []:
draw_board(board)
print_score(board, player_tile, computer_tile)
draw_text("Click where you wish to place your counter", 50, "centre", (screen_size/2),(screen_size - (margin/2)))
pygame.display.update()
move = get_player_move(board, player_tile)
make_move(board, player_tile, move[0], move[1])
draw_board(board)
print_score(board, player_tile, computer_tile)
pygame.display.update()
clock.tick(fps)
turn = 'computer'
elif turn == 'computer':
if computer_valid_moves != []:
while True:
draw_board(board)
print_score(board, player_tile, computer_tile)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
w = 400
h = 60
ic = WHITE
ac = PALE_GREY
if int(screen_size/2)+w/2 > mouse[0] > int(screen_size/2)-w/2 and int(screen_size - margin/2)+h/2 > mouse[1] > int(screen_size - margin/2)-h/2:
pygame.draw.rect(window, ac, ((int(screen_size/2)-w/2),(int(screen_size - margin/2)-h/2), w, h))
if click[0] == 1:
break
else:
pygame.draw.rect(window, ic, ((int(screen_size/2)-w/2),(int(screen_size - margin/2)-h/2), w, h))
basicFont = pygame.font.SysFont('comicsans', 40)
text = basicFont.render("Click for Computer's move", True, BLACK,)
textRect = text.get_rect()
textRect.center = (int(screen_size/2)),(int(screen_size - margin/2))
window.blit(text, textRect)
pygame.display.update()
clock.tick(fps)
move = get_computer_move(board, computer_tile)
make_move(board, computer_tile, move[0], move[1])
draw_board(board)
print_score(board, player_tile, computer_tile)
pygame.display.update()
clock.tick(fps)
turn = 'player'
def intro():
global turn
intro = True
turn = who_goes_first()
first = 'The ' + turn + ' will go first.'
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
window.fill(GREEN)
draw_text('Othello', 115, 'centre', screen_size/2, screen_size/5)
draw_text(first, 80,'centre', screen_size/2, screen_size*2/5)
draw_text('Choose your colour to start', 80, 'centre', screen_size/2, screen_size*3/5)
button("Black", screen_size/3, screen_size*4/5, 100, 50,BLACK, DARK_GREY, WHITE , player_black)
button("White", screen_size*2/3, screen_size*4/5, 100, 50,WHITE, PALE_GREY, BLACK , player_white)
pygame.display.update()
clock.tick(fps)
def game_loop(player_tile, computer_tile):
while True:
final_board = play_game(player_tile, computer_tile)
while True:
# Display the final score
draw_board(final_board)
scores = get_score_of_board(final_board)
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
quit()
if scores[player_tile] > scores[computer_tile]:
player_win = 'You beat the computer by ' + str(scores[player_tile] - scores[computer_tile]) + ' points. Congratulations!'
draw_text(player_win, 50, 'centre', (screen_size/2),(margin/2))
elif scores[player_tile] < scores[computer_tile]:
computer_win = 'You lost. The computer beat you by ' + str(scores[computer_tile] - scores[player_tile]) + ' points.'
draw_text(computer_win, 50, 'centre', (screen_size/2),(margin/2))
else:
draw_text('The game was a tie.', 50, 'centre', (screen_size/2),(margin/2))
button('Play again', (screen_size/3), (screen_size-(margin/2)), 150, 60, BLACK, DARK_GREY, WHITE , intro)
button('Quit',(screen_size*2/3), (screen_size-(margin/2)), 150, 60, WHITE, PALE_GREY, BLACK, quit_game)
pygame.display.update()
clock.tick(fps)
def quit_game():
pygame.quit()
quit()
intro()
I have update to MacOS 10.15.5 and still have the same problem. However I have tried it on a system running MacOS 10.14.6 (using the same versions of python, py2app and pygame) and the app produces works OK but gives the error when quit (as the app produced using -A on MacOS 10.15.4 did).
Sorry about the slow response.
This "should" just work and I'm not sure why it doesn't.
Annoyingly (?) the program works for me, although I did have to fix the script (quit_gamecalls a non-existent global function quit()). I still get an py2app generated error pop-up when closing the application. The usual way to debug this is to start the application in a shell (dist/Othello.app/Contets/MacOS/Othello), but for some reason that doesn't work reliably for me (most of the time the GUI doesn't actually launch until I quit the script using CTRL+C, which defeats the purpose of launching the script like this).