pythonpygamepyinstallernotimplementedexception

PyGame NotImplementedError


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.


Solution

  • 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