问题
I made a pygame application using PyInstaller but for some reason when I open it it raises the NotImplementedError, if found out that this error is being raised in the _has function on line 1663 of init.py in pkg-resources but i'm not sure why its being called. I tried to trace it back as far as I can and so far i cant find and connection between pkg-resources and pygame. The exe worked with the previous version, which didn't include text so i tried importing pygame._view but that didn't work. I only get this error with the executable, when running it in visual studio or the python IDE it works perfectly. Here is my game code:
import pygame as p, random as r, time as t, sys, tkinter as tk
if False:
import p._view
width, height = 640, 480 #Initial screen width & height
x, y, vel = 0, 0, [1, 1] #Makes coordinates and velocity
showInfo = False #sets bool to show display info
fullscr = False #sets bool to toggle full screen
iter = False #sets iteration bool for fullscreen toggles
catch = False #sets catch bool for fullscreen toggles
showHelp = False #sets the bool to bring up the help menu
showMenuHelp = True #sets the bool to toggle the --Press H for help--
c = 0 #sets counter for corner hits
h = 0 #sets counter for total hits
corners = [(width-29, height-19), (29, 19), (width-29, 19), (29, height-19)] #defiones list of all corner coordinates
helpmsg = ["----Help----", "F3: Show live in-game information", "F11: Fullscreen toggle", "r: set the logo to the center of the screen", "h: Toggle this menu"] #defines list of lines in the help message
DVD = p.image.load('sprites\\w.png') #Loads a sprite
DVDRECT = DVD.get_rect() #Makes object for the sprites to be loaded onto
p.display.set_caption('DVD')#Sets executable capton
screen = p.display.set_mode((width, height), p.RESIZABLE) #Sets screen to resizable mode
fps = 90 #sets FPS
clock = p.time.Clock() #sets FPS clock
p.init() #Initialize Pygame
Font = p.font.Font('freesansbold.ttf', 12) #initializes font
more = Font.render("--Press H for help--", True, (255, 255, 255)) #makes default on boot helper
morerect = more.get_rect() #makes surface for default on boot helper
x, y = r.choice([570, 571, 572]), r.choice([420, 421, 422]) #sets the start location
#Loads in sprites
wht = p.image.load('sprites\\w.png')
blu = p.image.load('sprites\\b.png')
pnk = p.image.load('sprites\\p2.png')
pur = p.image.load('sprites\\p.png')
grn = p.image.load('sprites\\g.png')
org = p.image.load('sprites\\o.png')
ylw = p.image.load('sprites\\y.png')
p.display.set_icon(wht)
def new_color():
"""
Function for getting random colors
"""
return r.choice([wht, blu, pnk, pur, grn, org, ylw])
def get_info():
"""
Function for getting in game live information
"""
info = p.display.Info() #creates object to get information
mem = str(info.video_mem) + "mb" #gets vram being used
#checks for accelerated hardware
if info.hw:
accel = "Accerated Hardware: True"
else:
accel = "Accerated Hardware: False"
#checks to see if windowed display modes are availble
if info.wm:
disMode = "Window Options: True"
else:
disMode = "Window Options: False"
curPosX, curPosY = DVDRECT.center[0], DVDRECT.center[1] #gets current position of logo
curW, curH = info.current_w, info.current_h #gets current width and height of window
fps = round(clock.get_fps())#gets current fps (in interger)
#checks if there is no vram
if mem == "0mb":
mem = "Unknown"
#gets current color of the DVD logo
if DVD == wht:
color = "Color: White"
if DVD == blu:
color = "Color: Blue"
if DVD == pnk:
color = "Color: Pink"
if DVD == pur:
color = "Color: Purple"
if DVD == grn:
color = "Color: Green"
if DVD == org:
color = "Color: Orange"
if DVD == ylw:
color = "Color: Yellow"
hits = "Total hits: "+str(h) #gets total hits
corner = "Corner hits: "+str(c) #gets corner hits
return ["Memory Use: "+str(mem),"DVD X: "+str(curPosX)+" Y: "+str(curPosY), "Screen Width: "+str(curW)+" Height: "+str(curH), "FPS: "+str(fps), hits, corner, accel, disMode, color]
while True:
for event in p.event.get():
#Exits if user closes window
if event.type == p.QUIT:
print("exiting")
p.quit()
sys.exit()
if event.type == p.KEYUP:
#toggles info menu
if event.key == p.K_F3:
if showInfo:
print("hiding info")
showInfo = False
else:
print("showing info")
showInfo = True
#toggles fullscreen
if event.key == p.K_F11:
if True != fullscr:
fullscr = True
iter = False
root = tk.Tk()
scrw = root.winfo_screenwidth()
scrh = root.winfo_screenheight()
if width != scrw or scrh != height:
screen = p.display.set_mode((scrw, scrh), p.RESIZABLE)
else:
screen = p.display.set_mode((0, 0), p.FULLSCREEN)
else:
fullscr = False
screen = p.display.set_mode((640, 480), p.RESIZABLE)
#resets the logo to the center of the window
if event.key == p.K_r:
info = p.display.Info()
x, y = round(info.current_w/2), round(info.current_h/2)
#toggles help menu
if event.key == p.K_h:
if showHelp:
showHelp = False
else:
showHelp = True
showMenuHelp = False
#toggles default on boot helper
if event.key == p.K_s:
if showMenuHelp:
showMenuHelp = False
else:
showMenuHelp = True
#checks if the user changed window dimensions and adjust the game surface accordingly
if event.type == p.VIDEORESIZE:
scrsize = event.size
screen = p.display.set_mode(scrsize, p.RESIZABLE)
width, height = scrsize[0], scrsize[1]
if DVDRECT.center[0] >= width-29:
y = DVDRECT.center[1]
x = width-30
DVDRECT.center = (x, y)
if DVDRECT.center[1] >= height-19:
y = height-20
DVDRECT.center = (x, y)
corners = [(width-29, height-19), (29, 19), (width-29, 19), (29, height-19)]
#Makes new coordinates:
x += vel[0]
y += vel[1]
#checks if logo hits a corner
if (x, y) in corners:
print("corner")
c += 1
#Checks if logo hits a wall
if x >= width-29:
print("right")
vel[0] = -vel[0] #Makes logo 'bounce' off wall
DVD = new_color() #Sets a new color to the logo
p.display.set_icon(DVD) #Sets icon to same color as logo
h += 1 #Adds one total hit counter
if x <= 29:
print("left")
vel[0] = -vel[0]
DVD = new_color()
p.display.set_icon(DVD)
h += 1
if y >= height-19:
print("bottom")
vel[1] = -vel[1]
DVD = new_color()
p.display.set_icon(DVD)
h += 1
if y <= 19:
print("top")
vel[1] = -vel[1]
DVD = new_color()
p.display.set_icon(DVD)
h += 1
DVDRECT.center = (x, y) #moves the logo
screen.fill((0, 0, 0)) #sets background to black
screen.blit(DVD, DVDRECT) #Updates logo
Iy = 6 #sets text starting Y coordinate
#shows live info menu
if showInfo:
info = get_info()
for i in info:
curIn = Font.render(i, True, (255,255,255))
curInRect = curIn.get_rect()
curInRect.center = (round(curInRect.w/2), Iy)
screen.blit(curIn, curInRect)
Iy += 12
#shows help menu
if showHelp:
for i in helpmsg:
help = Font.render(i, True, (255, 255, 255))
helprect = help.get_rect()
helprect.center = (round(helprect.w/2), Iy)
screen.blit(help, helprect)
Iy += 12
else:
#shows default on boot helper
if showMenuHelp:
morerect.center = (round(morerect.w/2), Iy)
screen.blit(more, morerect)
#sets to fullscreen 1 frame after surface is resized to the display resolution
if fullscr and iter and catch:
screen = p.display.set_mode((0, 0), p.FULLSCREEN)
catch = False
#sets bools to make the previous IF statment run in the next frame
if fullscr and iter != True:
iter = True
catch = True
p.display.update() #updates screen
clock.tick(fps) #updates fps clock
I also tried pasting the pygame folder with freesansbold.ttf but that didn't work either. The error messages shows as follows:
Traceback (most recent call last):
File "DVD.py", line 25, in <module>
File "site-packages\pygame\pkgdata.py", line 50, in getResource
File "site-packages\pkg-resources\__init__.py", line 1150, in resource_exists
File "site-packages\pkg-resources\__init__.py", line 1608, in has_resource
File "site-packages\pkg-resources\__init__.py", line 1663, in _has
NotImplementedError: Can't perform this operation for unregistered loader type [696] Failed to execute script DVD
I will be doing as much as can to find out why this is happening, as help is appreciated. I can also put the files on GitHub if need be.
回答1:
I finally figured it out, I had to save the ttf file in the same directory as the code. This works for compiling the application into a folder, but for one file you have to change the code.
def rp(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
upon adding this function you must call it with the name and extension of the file you wish to use, like:
rp('freesansbold.ttf')
to initialize the font just do this:
Font = p.font.Font(rp('freesansbold.ttf'), 12) #initializes font
it works to same for the images I was using too, but i had the take them out to the Sprites folder and package each picture individually with the program. Here's the spec file for Pyinstaller:
# -*- mode: python -*-
block_cipher = None
a = Analysis(['DVD.py'],
pathex=['C:\\Users\\Kaden\\Desktop\\dvd builds'],
binaries=[],
datas=[('./w.png', '.'), ('./b.png', '.'), ('./g.png', '.'), ('./o.png', '.'), ('./p.png', '.'), ('./p2.png', '.'), ('./y.png', '.'), ('./freesansbold.ttf', '.')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='DVD',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False , icon='icon.ico')
then just call pyinstaller like you usually would:
pyinstaller DVD.spec
来源:https://stackoverflow.com/questions/53917004/pygame-notimplementederror