Python Forum
python limitations?
Thread Rating:
  • 1 Vote(s) - 1 Average
  • 1
  • 2
  • 3
  • 4
  • 5
python limitations?
#1
I am just starting to learn to program, but I found that if I draw an image on my screen, pygame only runs at 7 fps. I am told that this is normal, and I should optimize, which is what I shall do. Yet it occurs to me, other games update the entire screen every tenth of a second. The entire screen, as the view has to change as the character moves around. How is this done without requiring a supercomputer? Or is python just rather limited in what it can do? I genuinely do not know, any input would be appreciated.
Reply
#2
Quote: I found that if I draw an image on my screen, pygame only runs at 7 fps. I am told that this is normal, and I should optimize, which is what I shall do.
This is absolutely not normal. Pygame and Python are not fast enough for some games, certainly, but you should have no issues maintaining 60 fps until you reach much higher levels of complexity. If possible please share a minimal example that exhibits this behavior (with any needed resources, images, etc) so that we can reproduce it and better assist you.
Reply
#3
ok, so this is the code that is running like a 100 year old stroke victim
import pygame
from pygame.locals import *
import os
import Keyhandler
import Menues

x, y = 0, 0
position = (0, 30)
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])

infoObject = pygame.display.Info()
screenxsize, screenysize = infoObject.current_w, (infoObject.current_h - 30)
running, time, leftmousepress, mouseclicked,  mouselefthold, keypressed, screenresizedyet, keycombopressed, helptext, pressed, clickedon, menuenum = True, 0, False, False, False, False, False, False, " ", False, False, "Start"
mpos, mouseheld, key, time = (0,0), False, [], 0
font = pygame.font.SysFont('Comic Sans MS', 10)

pygame.init()
pygame.font.init()

screen = pygame.display.set_mode([(int(screenxsize)), (int(screenysize))], RESIZABLE)
pygame.display.set_caption('Dnd reimagined')






while running:
    time = time + 1
    running, key, keypressed, keysheld, keycombo, mouseclicked, mouseunclicked, mouseheld, mpos, screenxsize, screenysize = Keyhandler.keyhandler(screenxsize, screenysize, mouseclicked)
    menuenum = Menues.drawmenues(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, menuenum, time)


    Menues.drawmenues(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, menuenum, time)

    if len(key) > 0:
        print(key)
    if mouseclicked == True:
        print("CLICK")

    print("loop")

    pygame.display.update()
import pygame
from pygame.locals import *
import time


mpos, mouseheld = False, False
menuenum = "Start"


pygame.init()
pygame.font.init()
font = pygame.font.SysFont('Comic Sans MS', 10)

def drawbutton(screen, mpos, mouseheld, mouseclicked, clickedon, posx, posy, buttonwidth, buttonheight, text):
    buttontext = font.render(text, False, (255, 255, 255))
    if clickedon == True:
        pygame.draw.rect(screen, (100, 10, 100),  (posx, posy, buttonwidth, buttonheight))
        pygame.draw.rect(screen, (100, (100), 100), (posx + (buttonwidth * .1), posy + (buttonheight * .1), (buttonwidth * .8), (buttonheight * .8)))
        screen.blit(buttontext, (posx, posy))

    else:
        pygame.draw.rect(screen, (100, 100, 100), (posx, posy, buttonwidth, buttonheight))
        pygame.draw.rect(screen, (100, 10, 100), (posx + (buttonwidth * .1), posy + (buttonheight * .1), (buttonwidth * .8), (buttonheight * .8)))
        screen.blit(buttontext, (posx, posy))




def drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text):
    clickedon = False
    if ((posx < mpos[0] < (posx + buttonwidth)) & (posy < mpos[1] < (posy + buttonheight))):
        if mouseclicked == True:
            clickedon = True
        else:
            clickedon = False
    else: held = False
    drawbutton(screen, mpos, mouseheld, mouseclicked, clickedon, posx, posy, buttonwidth, buttonheight, text)
    return clickedon

def startmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time):
    Startscreen = pygame.image.load('Dnd.png')
    screen.blit(pygame.transform.scale(Startscreen, (screenxsize, screenysize)), (0, 0))
    print("blit")
    menuenum = "Start"
    buttonwidth = screenxsize // 20
    buttonheight = screenysize // 20
    posx = (screenxsize // 2) - buttonwidth
    posy = (screenysize // 2) - buttonheight
    text = "Start"

    drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
    clickedon = drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
    #if clickedon == True:
       # menuenum = "Charactorcreatior"

    buttonwidth = screenxsize // 20
    buttonheight = screenysize // 20
    posx = (screenxsize) - buttonwidth
    posy = (screenysize) - buttonheight
    text = "Help"

    drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
    clickedon = drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
    #if clickedon == True:
        #menuenum = "help"

    return menuenum



def helpmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time):
    time = time + 1
    print(time)

    menuenum = "help"
    buttonwidth = screenxsize // 20
    buttonheight = screenysize // 20
    posx = time
    posy = time
    text = "Back"

    drawanddetect(screen, mpos, mouseclicked, mouseheld, mouseunclicked, posx, posy, buttonwidth, buttonheight, text)
    clickedon = drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
    if clickedon == True:
        menuenum = "Start"

    return menuenum, time



def drawmenues(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, menuenum, time):
    menuenum = "help"
    if menuenum == "help":
        print("fruiof")
        startmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time)
        menuenum = startmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time)
    if menuenum == "Start":
        menuenum, time = helpmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time)
        helpmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseclicked, mouseheld, time)



    return menuenum
import pygame
from pygame.locals import *
import os

pygame.init()

def keyhandler(screenxsize, screenysize, mouseclicked):
    keycombo = False
    running = True
    keypressed = False
    key = []
    keysheld = pygame.key.get_pressed()
    mousestate = pygame.mouse.get_pressed()
    mouseheld = mousestate[0]
    opressed = False
    mpos = pygame.mouse.get_pos()
    bob = 0
    mouseclicked = []
    mouseunclicked = []

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            print("Quitting")
        if event.type == pygame.KEYDOWN:
            keypressed = True
            if keysheld[K_LSHIFT]:
                caps = True
            else:
                caps = False
            print("keypressed")
            if event.key == pygame.K_a:
                if caps == True:
                    key.append("A")
                else:
                    key.append("a")
            if event.key == pygame.K_b:
                if caps == True:
                    key.append("B")
                else:
                    key.append("b")
            if event.key == pygame.K_c:
                if caps == True:
                    key.append("C")
                else:
                    key.append("c")
            if event.key == pygame.K_d:
                if caps == True:
                    key.append("D")
                else:
                    key.append("d")
            if event.key == pygame.K_e:
                if caps == True:
                    key.append("E")
                else:
                    key.append("e")
            if event.key == pygame.K_f:
                if caps == True:
                    key.append("F")
                else:
                    key.append("f")
            if event.key == pygame.K_g:
                if caps == True:
                    key.append("G")
                else:
                    key.append("g")
            if event.key == pygame.K_h:
                if caps == True:
                    key.append("H")
                else:
                    key.append("h")
            if event.key == pygame.K_i:
                if caps == True:
                    key.append("I")
                else:
                    key.append("i")
            if event.key == pygame.K_j:
                if caps == True:
                    key.append("J")
                else:
                    key.append("j")
            if event.key == pygame.K_k:
                if caps == True:
                    key.append("K")
                else:
                    key.append("k")
            if event.key == pygame.K_l:
                if caps == True:
                    key.append("L")
                else:
                    key.append("l")
            if event.key == pygame.K_m:
                if caps == True:
                    key.append("M")
                else:
                    key.append("m")
            if event.key == pygame.K_n:
                if caps == True:
                    key.append("N")
                else:
                    key.append("n")
            if event.key == pygame.K_o:
                if caps == True:
                    key.append("O")
                else:
                    key.append("o")
                    opressed = True
                    if keysheld[K_p]:
                        keycombo = "op"
            else:
                keycombo = []
            if event.key == pygame.K_p:
                if caps == True:
                    key.append("P")
                else:
                    key.append("p")
                    if opressed == True:
                        keycombo = "op"
                    if keysheld[K_o]:
                        keycombo = "op"
            else:
                keycombo = []
            if event.key == pygame.K_q:
                if caps == True:
                    key.append("Q")
                else:
                    key.append("q")
            if event.key == pygame.K_r:
                if caps == True:
                    key.append("R")
                else:
                    key.append("r")
            if event.key == pygame.K_s:
                if caps == True:
                    key.append("S")
                else:
                    key.append("s")
            if event.key == pygame.K_t:
                if caps == True:
                    key.append("T")
                else:
                    key.append("t")
            if event.key == pygame.K_u:
                if caps == True:
                    key.append("U")
                else:
                    key.append("u")
            if event.key == pygame.K_v:
                if caps == True:
                    key.append("V")
                else:
                    key.append("v")
            if event.key == pygame.K_w:
                if caps == True:
                    key.append("W")
                else:
                    key.append("w")
            if event.key == pygame.K_x:
                if caps == True:
                    key.append("X")
                else:
                    key.append("x")
            if event.key == pygame.K_y:
                if caps == True:
                    key.append("Y")
                else:
                    key.append("y")
            if event.key == pygame.K_z:
                if caps == True:
                    key.append("Z")
                else:
                    key.append("z")
            if event.key == pygame.K_LSHIFT:
                key.append("LShift")
        if event.type == pygame.KEYUP:
            keypressed = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouseclicked.append(True)
        else:
            mouseclicked.append(False)
        if event.type == pygame.MOUSEBUTTONUP:
            mouseunclicked.append(True)
        else:
            mouseunclicked.append(False)
        if event.type == pygame.VIDEORESIZE:
            print("Resizing")
            screenxsize = event.w
            screenysize = event.h
    if True in mouseclicked:
        mouseclicked = True
    else:
        mouseclicked = False
    if True in mouseunclicked:
        mouseunclicked = True
    else:
        mouseunclicked = False
    key = ''.join(key)
    return running, key, keypressed, keysheld, keycombo, mouseclicked, mouseunclicked, mouseheld, mpos, screenxsize, screenysize
and this is the code i tried to replicate the problem with. i get that they are obviously different programs, but they both draw the screen once per tick. I do not know why the first if running so badly. As you said, it would have to be really complex to normally have this problem.
import pygame
from pygame.locals import *
import os
x, y = 0, 0
position = (0, 30)
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])
pygame.init()
infoObject = pygame.display.Info()
screenxsize, screenysize = infoObject.current_w, (infoObject.current_h - 30)



screen = pygame.display.set_mode([(int(screenxsize)), (int(screenysize))], RESIZABLE)


Startscreen = pygame.image.load('Dnd.png')
i = 0

while True:
    i = i + 1
    screen.blit(pygame.transform.scale(Startscreen, (screenxsize, screenysize)), (0, 0))
    pygame.draw.rect(screen, (100,100,100), (i, i, 50, 50))
    print("looped")
    pygame.display.update()
also this is probably in the wrong place now. I do not know how to fix that. Any help would be appreciated.
Reply
#4
Okay, I will try to take a closer look tomorrow but a few things jump out immediately.

Convert all images when you load them.
Startscreen = pygame.image.load('Dnd.png').convert()
or convert_alpha() if it has transparency.

Add a clock to your program.
clock = pg.time.Clock()
And at the end of your game loop once per frame call:
clock.tick(60)
Your primary slowdown is probably reloading resources.
It seems your menu function runs once per frame and reloads the image every time it runs.
Load and convert images and fonts once; not every iteration.

Also, we talked about your event handler in the other thread but another slowdown will be using all ifs in that function rather than elifs.

An event can only be one thing.
if event.type == pg.KEYDOWN:
    #do something
elif event.type == pg.MOUSEBUTTONDOWN: # Note elif not if as an event can't be both a KEYDOWN and a MOUSEBUTTONDOWN.
    #do something else
Currently your handler checks every single conditional for every single event rather than stopping once it finds the right one.

Start with some of that stuff and see how it goes.

Here is a repo of some examples I have written for folks like you.
https://github.com/Mekire/pygame-samples
Download the repo and see how that stuff runs for you.
Reply
#5
Example how you can improve key handler.
Reducing the ifs and adding elif will takes less time to process.
def keyhandler(screenxsize, screenysize):
    key = []
    running = True
    mouseclick = False
    mouseunclick = False
    for event in pygame.event.get()
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pgyame.KEYDOWN:
            # handle space, numbers, a-z, A-Z, .!@#$%^&*() etc
            if 32 <= event.key < 123:
                # handles cap
                key.append(event.unicode)
        elif event.type == pygame.MOUSEBUTTONDOWN:
            # event.button , 1 = left click, 2 = middle click, 3 = right click
            mouseclick = True
        elif event.type == pygame.MOUDEBUTTONUP:
            mouseunclick = True
        elif event.type == pygame.VIDEORESIZE:
            print("Resizing")
            screenxsize = event.w
            screenysize = event.h

    key_states = pygame.key.get_pressed()
    # combos
    keycombos = []
    if key_states[pygame.K_o] and key_states[pygame.K_p]:
        keycombos.append("op")

    return (running,
            ''.join(key),
            len(key) > 0, # if a key has been pressed
            key_states,
            keycombo,
            mouseclicked,
            mouseunclicked,
            pygame.mouse.get_pressed()[0],
            pygame.mouse.get_pos(),
            screenxsize,
            screenysize)
If at all possible. You don't want to use pygame.transform.scale every cycle.
This will slow your program down.
99 percent of computer problems exists between chair and keyboard.
Reply
#6
If you just want letters. You could do.
if 97 <= event.key < 123 or 65 <= event.key < 90:
    key.append(event.unicode)

Small error.
if 32 >= event.key > 123:
I should have wrote
if 32 <= event.key < 123:
99 percent of computer problems exists between chair and keyboard.
Reply
#7
(Aug-23-2018, 03:26 PM)Windspar Wrote: If you just want letters.
I handle this by creating a list (or set) of acceptable characters. This is more readable and more maintainable should the set change:
import string
ACCEPTED = string.ascii_letters + string.digits + string.punctuation + " "
And then in your event handler:
if event.unicode in ACCEPTED:
    self.buffer.append(event.unicode)
Reply
#8
@Mekire
I tested it. It eats home keys and arrows keys.
Do you do something different ?
from types import SimpleNamespace
import string
import pygame

ACCEPTED = string.ascii_letters + string.digits + string.punctuation + " "
def main():
    pygame.init()
    pygame.display.set_caption('Testing string import')
    rect = pygame.Rect(0, 0, 400, 300)
    surface = pygame.display.set_mode(rect.size)
    clock = pygame.time.Clock()
    running = True
    font = pygame.font.Font(None, 24)
    text = SimpleNamespace(**{'image':None,
        'repr_image': None,
        'key_image': None,
        'buffer':[],
        'key_buffer':[]})
    while running:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.unicode in ACCEPTED:
                    text.buffer.append(event.unicode)
                    text.key_buffer.append(event.key)
                elif event.key == pygame.K_BACKSPACE:
                    if len(text.buffer) > 1:
                        text.buffer = text.buffer[:-1]
                        text.key_buffer = text.key_buffer[:-1]
                    else:
                        text.buffer = []
                        text.key_buffer = []
                elif event.key == pygame.K_LEFT:
                    print('Key Left')

                text.image = font.render(''.join(text.buffer), 1, (0,200,0))
                text.repr_image = font.render(str(text.buffer), 1, (200, 100, 0))
                text.key_image = font.render(str(text.key_buffer), 1, (0, 100, 200))

            elif event.type == pygame.QUIT:
                running = False

        surface.fill((0,0,0))
        if text.image:
            surface.blit(text.image, (10, 50))
            surface.blit(text.repr_image, (10, 100))
            surface.blit(text.key_image, (10, 150))

        pygame.display.update()
        clock.tick(30)

    pygame.quit()

main()

I have to add event.unicode != '' to work.
if event.unicode in ACCEPTED and event.unicode != '':
99 percent of computer problems exists between chair and keyboard.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020