Paint program made in PyGame











up vote
10
down vote

favorite












I am making a paint program in Python using Pygame. Right now the program is very laggy, and I wanted to get my code reviewed incase the reason is in the way I have written the code.



EDIT:
I was mostly able to fix the performance issue by changing the way layers work. The issue was too much blitting and I fixed it by showing everything on the active layer and only blitting that. It would still be useful if someone reviewed the code.



(Code below is the updated version)



import pygame as pg
import pygame.gfxdraw
import sys,os,math

pg.init()
pg.event.set_allowed([pg.QUIT,pg.MOUSEMOTION,pg.MOUSEBUTTONDOWN,pg.KEYDOWN])

# Settings:
clock = pygame.time.Clock()
fps = 120
font = pg.font.SysFont('consolas',20)
screenres = (500,500)
realres = (screenres[0]*1.2,screenres[1]*1.2)

updated = False
dirtyrects =

# Colors | R | G | B | A |
clear = ( 0, 0, 0, 0)
white = (255,255,255)
gray = (150,150,150)
black = ( 0, 0, 0)
red = (255, 0, 0)
orange = (255,125, 0)
yellow = (255,255, 0)
green = ( 0,225, 0)
blue = ( 0, 0,255)
purple = (150, 0,150)

colors = [black,white,red,orange,yellow,green,blue,purple]

numkey = [
pg.K_1,
pg.K_2,
pg.K_3,
pg.K_4,
pg.K_5,
pg.K_6,
pg.K_7,
pg.K_8
]

# Surfaces:
window = pg.display.set_mode(screenres,pg.DOUBLEBUF)
window.fill(white)
canvas = pg.Surface((realres[0],realres[1]*0.84)).convert_alpha()
canvas.fill(white)
latest1 = canvas.copy()
latest2 = canvas.copy()
latest3 = canvas.copy()
latest4 = canvas.copy()
latest5 = canvas.copy()
layers = [latest1,latest2,latest3,latest4,latest5]
for layer in layers:
layer.fill(clear)
overlay = pg.Surface(screenres).convert_alpha()

# Rects:
realrect = pg.Rect(0,0,realres[0],int(realres[1]*0.84))
screenrect = pg.Rect(0,0,screenres[0],int(screenres[1]*0.84))
toolbar = pg.Rect(0,420,500,80)

r = 25
clr = black
startpoint = None
endpoint = None
ongoing = False
undone = 0
maxundone = 0
holdingclick = False

def button(color,rect):
global clr,holdingclick
if 0 <= rect <= 9:
rect = pg.Rect(48*rect+12,446,44,44)
if pg.mouse.get_pressed()[0] and rect.collidepoint(mousepos) and not holdingclick:
clr = color
dirtyrects.append(toolbar)
if clr == color:
pg.draw.rect(overlay,color,rect)
pg.draw.rect(overlay,black,rect,3)
else:
pg.draw.rect(overlay,color,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8))
pg.draw.rect(overlay,black,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8),3)

def drawline():
global startpoint,endpoint,start
if startpoint == None:
startpoint = x,y
endpoint = x,y
if r > 1:
if startpoint != endpoint:
dx,dy = endpoint[0]-startpoint[0],endpoint[1]-startpoint[1]
angle = math.atan2(-dy,dx)%(2*math.pi)
dx,dy = math.sin(angle)*(r*0.999),math.cos(angle)*(r*0.999)
a = startpoint[0]+dx,startpoint[1]+dy
b = startpoint[0]-dx,startpoint[1]-dy
c = endpoint[0]-dx,endpoint[1]-dy
d = endpoint[0]+dx,endpoint[1]+dy
pointlist = [a,b,c,d]
pg.draw.polygon(latest1,clr,pointlist)
pg.draw.circle(latest1,clr,(x,y),r)
else:
pg.draw.line(latest1,clr,startpoint,endpoint,r)
startpoint = x,y

def shiftdown():
for layer in reversed(layers):
if layer == latest5:
canvas.blit(latest5,(0,0))
else:
layers[layers.index(layer)+1].blit(layer,(0,0))

def shiftup():
for layer in layers:
if layer == latest5:
layer.fill(clear)
else:
layer.fill(clear)
layer.blit(layers[layers.index(layer)+1],(0,0))

# Drawing static parts of overlay:
overlay.fill(clear)
pg.draw.rect(overlay,gray,toolbar)
pg.draw.rect(overlay,black,toolbar,3)

# Drawing number indicators for colors:
for color in colors:
text = font.render(str(colors.index(color)+1),True,black)
overlay.blit(text,(48*colors.index(color)+28,424))

overlaybg = overlay.copy()

while True:
for event in pg.event.get():
if event.type == pg.QUIT or pg.key.get_pressed()[pg.K_ESCAPE]:
pg.quit()
sys.exit()

if event.type == pg.MOUSEMOTION:
mousepos = pg.mouse.get_pos()
x = int(mousepos[0]*(realres[0]/screenres[0]))
y = int(mousepos[1]*(realres[1]/screenres[1]))
holdingclick = True
if screenrect.collidepoint(mousepos):
dirtyrects.append(screenrect)

if event.type == pg.MOUSEBUTTONDOWN:
holdingclick = False
if screenrect.collidepoint(mousepos):
dirtyrects.append(screenrect)

# Changing brush size:
if event.button == 4 and r < 100:
r += 1
dirtyrects.append(screenrect)
elif event.button == 5 and r > 2:
r -= 1
dirtyrects.append(screenrect)

if event.type == pg.KEYDOWN:

if event.key in numkey:
clr = colors[numkey.index(event.key)]
dirtyrects.append(toolbar)

# Emptying canvas:
if event.key == pg.K_e:
canvas.fill(white)
latest5.fill(clear)
latest4.fill(clear)
latest3.fill(clear)
latest2.fill(clear)
latest1.fill(clear)
undone = 0
maxundone = 0
dirtyrects.append(screenrect)

# Undoing and redoing:
if event.key == pg.K_u and undone < maxundone:
undone += 1
dirtyrects.append(screenrect)
if event.key == pg.K_i and undone > 0:
undone -= 1
dirtyrects.append(screenrect)

# Painting:
if pg.mouse.get_pressed()[0] and screenrect.collidepoint(mousepos):
if not ongoing:
while undone > 0:
shiftup()
undone -= 1
maxundone -= 1
shiftdown()
drawline()
ongoing = True
else:
startpoint = None
if ongoing:
if maxundone < 5:
maxundone += 1
ongoing = False

if screenrect in dirtyrects:

# Drawing canvas:
window.fill(white)
for layer in layers:
if layers.index(layer) == undone:
window.blit(pg.transform.smoothscale(layer,(screenrect[2],screenrect[3])),screenrect)

# Drawing overlay:
overlay.fill(clear)
if r > 1:
pg.gfxdraw.aacircle(overlay,mousepos[0],mousepos[1],int(r*screenres[0]/realres[0]),gray)
overlay.blit(overlaybg,screenrect)
for color in colors:
button(color,colors.index(color))
window.blit(overlay,screenrect)

pg.display.set_caption('Draw | FPS: ' + str(int(clock.get_fps())))
clock.tick(fps)

# Updating display:
if not updated:
pg.display.update()
updated = True
pg.display.update(dirtyrects)
dirtyrects.clear()









share|improve this question




























    up vote
    10
    down vote

    favorite












    I am making a paint program in Python using Pygame. Right now the program is very laggy, and I wanted to get my code reviewed incase the reason is in the way I have written the code.



    EDIT:
    I was mostly able to fix the performance issue by changing the way layers work. The issue was too much blitting and I fixed it by showing everything on the active layer and only blitting that. It would still be useful if someone reviewed the code.



    (Code below is the updated version)



    import pygame as pg
    import pygame.gfxdraw
    import sys,os,math

    pg.init()
    pg.event.set_allowed([pg.QUIT,pg.MOUSEMOTION,pg.MOUSEBUTTONDOWN,pg.KEYDOWN])

    # Settings:
    clock = pygame.time.Clock()
    fps = 120
    font = pg.font.SysFont('consolas',20)
    screenres = (500,500)
    realres = (screenres[0]*1.2,screenres[1]*1.2)

    updated = False
    dirtyrects =

    # Colors | R | G | B | A |
    clear = ( 0, 0, 0, 0)
    white = (255,255,255)
    gray = (150,150,150)
    black = ( 0, 0, 0)
    red = (255, 0, 0)
    orange = (255,125, 0)
    yellow = (255,255, 0)
    green = ( 0,225, 0)
    blue = ( 0, 0,255)
    purple = (150, 0,150)

    colors = [black,white,red,orange,yellow,green,blue,purple]

    numkey = [
    pg.K_1,
    pg.K_2,
    pg.K_3,
    pg.K_4,
    pg.K_5,
    pg.K_6,
    pg.K_7,
    pg.K_8
    ]

    # Surfaces:
    window = pg.display.set_mode(screenres,pg.DOUBLEBUF)
    window.fill(white)
    canvas = pg.Surface((realres[0],realres[1]*0.84)).convert_alpha()
    canvas.fill(white)
    latest1 = canvas.copy()
    latest2 = canvas.copy()
    latest3 = canvas.copy()
    latest4 = canvas.copy()
    latest5 = canvas.copy()
    layers = [latest1,latest2,latest3,latest4,latest5]
    for layer in layers:
    layer.fill(clear)
    overlay = pg.Surface(screenres).convert_alpha()

    # Rects:
    realrect = pg.Rect(0,0,realres[0],int(realres[1]*0.84))
    screenrect = pg.Rect(0,0,screenres[0],int(screenres[1]*0.84))
    toolbar = pg.Rect(0,420,500,80)

    r = 25
    clr = black
    startpoint = None
    endpoint = None
    ongoing = False
    undone = 0
    maxundone = 0
    holdingclick = False

    def button(color,rect):
    global clr,holdingclick
    if 0 <= rect <= 9:
    rect = pg.Rect(48*rect+12,446,44,44)
    if pg.mouse.get_pressed()[0] and rect.collidepoint(mousepos) and not holdingclick:
    clr = color
    dirtyrects.append(toolbar)
    if clr == color:
    pg.draw.rect(overlay,color,rect)
    pg.draw.rect(overlay,black,rect,3)
    else:
    pg.draw.rect(overlay,color,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8))
    pg.draw.rect(overlay,black,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8),3)

    def drawline():
    global startpoint,endpoint,start
    if startpoint == None:
    startpoint = x,y
    endpoint = x,y
    if r > 1:
    if startpoint != endpoint:
    dx,dy = endpoint[0]-startpoint[0],endpoint[1]-startpoint[1]
    angle = math.atan2(-dy,dx)%(2*math.pi)
    dx,dy = math.sin(angle)*(r*0.999),math.cos(angle)*(r*0.999)
    a = startpoint[0]+dx,startpoint[1]+dy
    b = startpoint[0]-dx,startpoint[1]-dy
    c = endpoint[0]-dx,endpoint[1]-dy
    d = endpoint[0]+dx,endpoint[1]+dy
    pointlist = [a,b,c,d]
    pg.draw.polygon(latest1,clr,pointlist)
    pg.draw.circle(latest1,clr,(x,y),r)
    else:
    pg.draw.line(latest1,clr,startpoint,endpoint,r)
    startpoint = x,y

    def shiftdown():
    for layer in reversed(layers):
    if layer == latest5:
    canvas.blit(latest5,(0,0))
    else:
    layers[layers.index(layer)+1].blit(layer,(0,0))

    def shiftup():
    for layer in layers:
    if layer == latest5:
    layer.fill(clear)
    else:
    layer.fill(clear)
    layer.blit(layers[layers.index(layer)+1],(0,0))

    # Drawing static parts of overlay:
    overlay.fill(clear)
    pg.draw.rect(overlay,gray,toolbar)
    pg.draw.rect(overlay,black,toolbar,3)

    # Drawing number indicators for colors:
    for color in colors:
    text = font.render(str(colors.index(color)+1),True,black)
    overlay.blit(text,(48*colors.index(color)+28,424))

    overlaybg = overlay.copy()

    while True:
    for event in pg.event.get():
    if event.type == pg.QUIT or pg.key.get_pressed()[pg.K_ESCAPE]:
    pg.quit()
    sys.exit()

    if event.type == pg.MOUSEMOTION:
    mousepos = pg.mouse.get_pos()
    x = int(mousepos[0]*(realres[0]/screenres[0]))
    y = int(mousepos[1]*(realres[1]/screenres[1]))
    holdingclick = True
    if screenrect.collidepoint(mousepos):
    dirtyrects.append(screenrect)

    if event.type == pg.MOUSEBUTTONDOWN:
    holdingclick = False
    if screenrect.collidepoint(mousepos):
    dirtyrects.append(screenrect)

    # Changing brush size:
    if event.button == 4 and r < 100:
    r += 1
    dirtyrects.append(screenrect)
    elif event.button == 5 and r > 2:
    r -= 1
    dirtyrects.append(screenrect)

    if event.type == pg.KEYDOWN:

    if event.key in numkey:
    clr = colors[numkey.index(event.key)]
    dirtyrects.append(toolbar)

    # Emptying canvas:
    if event.key == pg.K_e:
    canvas.fill(white)
    latest5.fill(clear)
    latest4.fill(clear)
    latest3.fill(clear)
    latest2.fill(clear)
    latest1.fill(clear)
    undone = 0
    maxundone = 0
    dirtyrects.append(screenrect)

    # Undoing and redoing:
    if event.key == pg.K_u and undone < maxundone:
    undone += 1
    dirtyrects.append(screenrect)
    if event.key == pg.K_i and undone > 0:
    undone -= 1
    dirtyrects.append(screenrect)

    # Painting:
    if pg.mouse.get_pressed()[0] and screenrect.collidepoint(mousepos):
    if not ongoing:
    while undone > 0:
    shiftup()
    undone -= 1
    maxundone -= 1
    shiftdown()
    drawline()
    ongoing = True
    else:
    startpoint = None
    if ongoing:
    if maxundone < 5:
    maxundone += 1
    ongoing = False

    if screenrect in dirtyrects:

    # Drawing canvas:
    window.fill(white)
    for layer in layers:
    if layers.index(layer) == undone:
    window.blit(pg.transform.smoothscale(layer,(screenrect[2],screenrect[3])),screenrect)

    # Drawing overlay:
    overlay.fill(clear)
    if r > 1:
    pg.gfxdraw.aacircle(overlay,mousepos[0],mousepos[1],int(r*screenres[0]/realres[0]),gray)
    overlay.blit(overlaybg,screenrect)
    for color in colors:
    button(color,colors.index(color))
    window.blit(overlay,screenrect)

    pg.display.set_caption('Draw | FPS: ' + str(int(clock.get_fps())))
    clock.tick(fps)

    # Updating display:
    if not updated:
    pg.display.update()
    updated = True
    pg.display.update(dirtyrects)
    dirtyrects.clear()









    share|improve this question


























      up vote
      10
      down vote

      favorite









      up vote
      10
      down vote

      favorite











      I am making a paint program in Python using Pygame. Right now the program is very laggy, and I wanted to get my code reviewed incase the reason is in the way I have written the code.



      EDIT:
      I was mostly able to fix the performance issue by changing the way layers work. The issue was too much blitting and I fixed it by showing everything on the active layer and only blitting that. It would still be useful if someone reviewed the code.



      (Code below is the updated version)



      import pygame as pg
      import pygame.gfxdraw
      import sys,os,math

      pg.init()
      pg.event.set_allowed([pg.QUIT,pg.MOUSEMOTION,pg.MOUSEBUTTONDOWN,pg.KEYDOWN])

      # Settings:
      clock = pygame.time.Clock()
      fps = 120
      font = pg.font.SysFont('consolas',20)
      screenres = (500,500)
      realres = (screenres[0]*1.2,screenres[1]*1.2)

      updated = False
      dirtyrects =

      # Colors | R | G | B | A |
      clear = ( 0, 0, 0, 0)
      white = (255,255,255)
      gray = (150,150,150)
      black = ( 0, 0, 0)
      red = (255, 0, 0)
      orange = (255,125, 0)
      yellow = (255,255, 0)
      green = ( 0,225, 0)
      blue = ( 0, 0,255)
      purple = (150, 0,150)

      colors = [black,white,red,orange,yellow,green,blue,purple]

      numkey = [
      pg.K_1,
      pg.K_2,
      pg.K_3,
      pg.K_4,
      pg.K_5,
      pg.K_6,
      pg.K_7,
      pg.K_8
      ]

      # Surfaces:
      window = pg.display.set_mode(screenres,pg.DOUBLEBUF)
      window.fill(white)
      canvas = pg.Surface((realres[0],realres[1]*0.84)).convert_alpha()
      canvas.fill(white)
      latest1 = canvas.copy()
      latest2 = canvas.copy()
      latest3 = canvas.copy()
      latest4 = canvas.copy()
      latest5 = canvas.copy()
      layers = [latest1,latest2,latest3,latest4,latest5]
      for layer in layers:
      layer.fill(clear)
      overlay = pg.Surface(screenres).convert_alpha()

      # Rects:
      realrect = pg.Rect(0,0,realres[0],int(realres[1]*0.84))
      screenrect = pg.Rect(0,0,screenres[0],int(screenres[1]*0.84))
      toolbar = pg.Rect(0,420,500,80)

      r = 25
      clr = black
      startpoint = None
      endpoint = None
      ongoing = False
      undone = 0
      maxundone = 0
      holdingclick = False

      def button(color,rect):
      global clr,holdingclick
      if 0 <= rect <= 9:
      rect = pg.Rect(48*rect+12,446,44,44)
      if pg.mouse.get_pressed()[0] and rect.collidepoint(mousepos) and not holdingclick:
      clr = color
      dirtyrects.append(toolbar)
      if clr == color:
      pg.draw.rect(overlay,color,rect)
      pg.draw.rect(overlay,black,rect,3)
      else:
      pg.draw.rect(overlay,color,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8))
      pg.draw.rect(overlay,black,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8),3)

      def drawline():
      global startpoint,endpoint,start
      if startpoint == None:
      startpoint = x,y
      endpoint = x,y
      if r > 1:
      if startpoint != endpoint:
      dx,dy = endpoint[0]-startpoint[0],endpoint[1]-startpoint[1]
      angle = math.atan2(-dy,dx)%(2*math.pi)
      dx,dy = math.sin(angle)*(r*0.999),math.cos(angle)*(r*0.999)
      a = startpoint[0]+dx,startpoint[1]+dy
      b = startpoint[0]-dx,startpoint[1]-dy
      c = endpoint[0]-dx,endpoint[1]-dy
      d = endpoint[0]+dx,endpoint[1]+dy
      pointlist = [a,b,c,d]
      pg.draw.polygon(latest1,clr,pointlist)
      pg.draw.circle(latest1,clr,(x,y),r)
      else:
      pg.draw.line(latest1,clr,startpoint,endpoint,r)
      startpoint = x,y

      def shiftdown():
      for layer in reversed(layers):
      if layer == latest5:
      canvas.blit(latest5,(0,0))
      else:
      layers[layers.index(layer)+1].blit(layer,(0,0))

      def shiftup():
      for layer in layers:
      if layer == latest5:
      layer.fill(clear)
      else:
      layer.fill(clear)
      layer.blit(layers[layers.index(layer)+1],(0,0))

      # Drawing static parts of overlay:
      overlay.fill(clear)
      pg.draw.rect(overlay,gray,toolbar)
      pg.draw.rect(overlay,black,toolbar,3)

      # Drawing number indicators for colors:
      for color in colors:
      text = font.render(str(colors.index(color)+1),True,black)
      overlay.blit(text,(48*colors.index(color)+28,424))

      overlaybg = overlay.copy()

      while True:
      for event in pg.event.get():
      if event.type == pg.QUIT or pg.key.get_pressed()[pg.K_ESCAPE]:
      pg.quit()
      sys.exit()

      if event.type == pg.MOUSEMOTION:
      mousepos = pg.mouse.get_pos()
      x = int(mousepos[0]*(realres[0]/screenres[0]))
      y = int(mousepos[1]*(realres[1]/screenres[1]))
      holdingclick = True
      if screenrect.collidepoint(mousepos):
      dirtyrects.append(screenrect)

      if event.type == pg.MOUSEBUTTONDOWN:
      holdingclick = False
      if screenrect.collidepoint(mousepos):
      dirtyrects.append(screenrect)

      # Changing brush size:
      if event.button == 4 and r < 100:
      r += 1
      dirtyrects.append(screenrect)
      elif event.button == 5 and r > 2:
      r -= 1
      dirtyrects.append(screenrect)

      if event.type == pg.KEYDOWN:

      if event.key in numkey:
      clr = colors[numkey.index(event.key)]
      dirtyrects.append(toolbar)

      # Emptying canvas:
      if event.key == pg.K_e:
      canvas.fill(white)
      latest5.fill(clear)
      latest4.fill(clear)
      latest3.fill(clear)
      latest2.fill(clear)
      latest1.fill(clear)
      undone = 0
      maxundone = 0
      dirtyrects.append(screenrect)

      # Undoing and redoing:
      if event.key == pg.K_u and undone < maxundone:
      undone += 1
      dirtyrects.append(screenrect)
      if event.key == pg.K_i and undone > 0:
      undone -= 1
      dirtyrects.append(screenrect)

      # Painting:
      if pg.mouse.get_pressed()[0] and screenrect.collidepoint(mousepos):
      if not ongoing:
      while undone > 0:
      shiftup()
      undone -= 1
      maxundone -= 1
      shiftdown()
      drawline()
      ongoing = True
      else:
      startpoint = None
      if ongoing:
      if maxundone < 5:
      maxundone += 1
      ongoing = False

      if screenrect in dirtyrects:

      # Drawing canvas:
      window.fill(white)
      for layer in layers:
      if layers.index(layer) == undone:
      window.blit(pg.transform.smoothscale(layer,(screenrect[2],screenrect[3])),screenrect)

      # Drawing overlay:
      overlay.fill(clear)
      if r > 1:
      pg.gfxdraw.aacircle(overlay,mousepos[0],mousepos[1],int(r*screenres[0]/realres[0]),gray)
      overlay.blit(overlaybg,screenrect)
      for color in colors:
      button(color,colors.index(color))
      window.blit(overlay,screenrect)

      pg.display.set_caption('Draw | FPS: ' + str(int(clock.get_fps())))
      clock.tick(fps)

      # Updating display:
      if not updated:
      pg.display.update()
      updated = True
      pg.display.update(dirtyrects)
      dirtyrects.clear()









      share|improve this question















      I am making a paint program in Python using Pygame. Right now the program is very laggy, and I wanted to get my code reviewed incase the reason is in the way I have written the code.



      EDIT:
      I was mostly able to fix the performance issue by changing the way layers work. The issue was too much blitting and I fixed it by showing everything on the active layer and only blitting that. It would still be useful if someone reviewed the code.



      (Code below is the updated version)



      import pygame as pg
      import pygame.gfxdraw
      import sys,os,math

      pg.init()
      pg.event.set_allowed([pg.QUIT,pg.MOUSEMOTION,pg.MOUSEBUTTONDOWN,pg.KEYDOWN])

      # Settings:
      clock = pygame.time.Clock()
      fps = 120
      font = pg.font.SysFont('consolas',20)
      screenres = (500,500)
      realres = (screenres[0]*1.2,screenres[1]*1.2)

      updated = False
      dirtyrects =

      # Colors | R | G | B | A |
      clear = ( 0, 0, 0, 0)
      white = (255,255,255)
      gray = (150,150,150)
      black = ( 0, 0, 0)
      red = (255, 0, 0)
      orange = (255,125, 0)
      yellow = (255,255, 0)
      green = ( 0,225, 0)
      blue = ( 0, 0,255)
      purple = (150, 0,150)

      colors = [black,white,red,orange,yellow,green,blue,purple]

      numkey = [
      pg.K_1,
      pg.K_2,
      pg.K_3,
      pg.K_4,
      pg.K_5,
      pg.K_6,
      pg.K_7,
      pg.K_8
      ]

      # Surfaces:
      window = pg.display.set_mode(screenres,pg.DOUBLEBUF)
      window.fill(white)
      canvas = pg.Surface((realres[0],realres[1]*0.84)).convert_alpha()
      canvas.fill(white)
      latest1 = canvas.copy()
      latest2 = canvas.copy()
      latest3 = canvas.copy()
      latest4 = canvas.copy()
      latest5 = canvas.copy()
      layers = [latest1,latest2,latest3,latest4,latest5]
      for layer in layers:
      layer.fill(clear)
      overlay = pg.Surface(screenres).convert_alpha()

      # Rects:
      realrect = pg.Rect(0,0,realres[0],int(realres[1]*0.84))
      screenrect = pg.Rect(0,0,screenres[0],int(screenres[1]*0.84))
      toolbar = pg.Rect(0,420,500,80)

      r = 25
      clr = black
      startpoint = None
      endpoint = None
      ongoing = False
      undone = 0
      maxundone = 0
      holdingclick = False

      def button(color,rect):
      global clr,holdingclick
      if 0 <= rect <= 9:
      rect = pg.Rect(48*rect+12,446,44,44)
      if pg.mouse.get_pressed()[0] and rect.collidepoint(mousepos) and not holdingclick:
      clr = color
      dirtyrects.append(toolbar)
      if clr == color:
      pg.draw.rect(overlay,color,rect)
      pg.draw.rect(overlay,black,rect,3)
      else:
      pg.draw.rect(overlay,color,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8))
      pg.draw.rect(overlay,black,(rect[0]+4,rect[1]+4,rect[2]-8,rect[3]-8),3)

      def drawline():
      global startpoint,endpoint,start
      if startpoint == None:
      startpoint = x,y
      endpoint = x,y
      if r > 1:
      if startpoint != endpoint:
      dx,dy = endpoint[0]-startpoint[0],endpoint[1]-startpoint[1]
      angle = math.atan2(-dy,dx)%(2*math.pi)
      dx,dy = math.sin(angle)*(r*0.999),math.cos(angle)*(r*0.999)
      a = startpoint[0]+dx,startpoint[1]+dy
      b = startpoint[0]-dx,startpoint[1]-dy
      c = endpoint[0]-dx,endpoint[1]-dy
      d = endpoint[0]+dx,endpoint[1]+dy
      pointlist = [a,b,c,d]
      pg.draw.polygon(latest1,clr,pointlist)
      pg.draw.circle(latest1,clr,(x,y),r)
      else:
      pg.draw.line(latest1,clr,startpoint,endpoint,r)
      startpoint = x,y

      def shiftdown():
      for layer in reversed(layers):
      if layer == latest5:
      canvas.blit(latest5,(0,0))
      else:
      layers[layers.index(layer)+1].blit(layer,(0,0))

      def shiftup():
      for layer in layers:
      if layer == latest5:
      layer.fill(clear)
      else:
      layer.fill(clear)
      layer.blit(layers[layers.index(layer)+1],(0,0))

      # Drawing static parts of overlay:
      overlay.fill(clear)
      pg.draw.rect(overlay,gray,toolbar)
      pg.draw.rect(overlay,black,toolbar,3)

      # Drawing number indicators for colors:
      for color in colors:
      text = font.render(str(colors.index(color)+1),True,black)
      overlay.blit(text,(48*colors.index(color)+28,424))

      overlaybg = overlay.copy()

      while True:
      for event in pg.event.get():
      if event.type == pg.QUIT or pg.key.get_pressed()[pg.K_ESCAPE]:
      pg.quit()
      sys.exit()

      if event.type == pg.MOUSEMOTION:
      mousepos = pg.mouse.get_pos()
      x = int(mousepos[0]*(realres[0]/screenres[0]))
      y = int(mousepos[1]*(realres[1]/screenres[1]))
      holdingclick = True
      if screenrect.collidepoint(mousepos):
      dirtyrects.append(screenrect)

      if event.type == pg.MOUSEBUTTONDOWN:
      holdingclick = False
      if screenrect.collidepoint(mousepos):
      dirtyrects.append(screenrect)

      # Changing brush size:
      if event.button == 4 and r < 100:
      r += 1
      dirtyrects.append(screenrect)
      elif event.button == 5 and r > 2:
      r -= 1
      dirtyrects.append(screenrect)

      if event.type == pg.KEYDOWN:

      if event.key in numkey:
      clr = colors[numkey.index(event.key)]
      dirtyrects.append(toolbar)

      # Emptying canvas:
      if event.key == pg.K_e:
      canvas.fill(white)
      latest5.fill(clear)
      latest4.fill(clear)
      latest3.fill(clear)
      latest2.fill(clear)
      latest1.fill(clear)
      undone = 0
      maxundone = 0
      dirtyrects.append(screenrect)

      # Undoing and redoing:
      if event.key == pg.K_u and undone < maxundone:
      undone += 1
      dirtyrects.append(screenrect)
      if event.key == pg.K_i and undone > 0:
      undone -= 1
      dirtyrects.append(screenrect)

      # Painting:
      if pg.mouse.get_pressed()[0] and screenrect.collidepoint(mousepos):
      if not ongoing:
      while undone > 0:
      shiftup()
      undone -= 1
      maxundone -= 1
      shiftdown()
      drawline()
      ongoing = True
      else:
      startpoint = None
      if ongoing:
      if maxundone < 5:
      maxundone += 1
      ongoing = False

      if screenrect in dirtyrects:

      # Drawing canvas:
      window.fill(white)
      for layer in layers:
      if layers.index(layer) == undone:
      window.blit(pg.transform.smoothscale(layer,(screenrect[2],screenrect[3])),screenrect)

      # Drawing overlay:
      overlay.fill(clear)
      if r > 1:
      pg.gfxdraw.aacircle(overlay,mousepos[0],mousepos[1],int(r*screenres[0]/realres[0]),gray)
      overlay.blit(overlaybg,screenrect)
      for color in colors:
      button(color,colors.index(color))
      window.blit(overlay,screenrect)

      pg.display.set_caption('Draw | FPS: ' + str(int(clock.get_fps())))
      clock.tick(fps)

      # Updating display:
      if not updated:
      pg.display.update()
      updated = True
      pg.display.update(dirtyrects)
      dirtyrects.clear()






      python performance python-3.x pygame






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 12 at 8:33

























      asked Nov 9 at 6:40









      Antti Tennivaara

      513




      513






















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote













          Sorry I don't have time rn to properly look all through your code for you but one quick thing I noticed is that it would be much nicer to put your colours in a dictionary. It could be implemented like:



          colours = {"black": (  0,  0,  0),
          "white": (255,255,255),
          ...}


          And then to get the colours you can just call colours["black"] rather than black which ties them all up together in one data structure which makes your code easier to understand and neater. Also, if you want to use your colors list which you currently have to iterate through (I don't know if you do), then you can use the new colours dictionary with .index()






          share|improve this answer





















          • Instead of building a table, just call pygame.Color('black').
            – Gareth Rees
            Nov 28 at 10:20










          • @GarethRees Does pygame have colours hard coded into it? I didn't know that
            – 13ros27
            Nov 28 at 10:58











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207284%2fpaint-program-made-in-pygame%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          0
          down vote













          Sorry I don't have time rn to properly look all through your code for you but one quick thing I noticed is that it would be much nicer to put your colours in a dictionary. It could be implemented like:



          colours = {"black": (  0,  0,  0),
          "white": (255,255,255),
          ...}


          And then to get the colours you can just call colours["black"] rather than black which ties them all up together in one data structure which makes your code easier to understand and neater. Also, if you want to use your colors list which you currently have to iterate through (I don't know if you do), then you can use the new colours dictionary with .index()






          share|improve this answer





















          • Instead of building a table, just call pygame.Color('black').
            – Gareth Rees
            Nov 28 at 10:20










          • @GarethRees Does pygame have colours hard coded into it? I didn't know that
            – 13ros27
            Nov 28 at 10:58















          up vote
          0
          down vote













          Sorry I don't have time rn to properly look all through your code for you but one quick thing I noticed is that it would be much nicer to put your colours in a dictionary. It could be implemented like:



          colours = {"black": (  0,  0,  0),
          "white": (255,255,255),
          ...}


          And then to get the colours you can just call colours["black"] rather than black which ties them all up together in one data structure which makes your code easier to understand and neater. Also, if you want to use your colors list which you currently have to iterate through (I don't know if you do), then you can use the new colours dictionary with .index()






          share|improve this answer





















          • Instead of building a table, just call pygame.Color('black').
            – Gareth Rees
            Nov 28 at 10:20










          • @GarethRees Does pygame have colours hard coded into it? I didn't know that
            – 13ros27
            Nov 28 at 10:58













          up vote
          0
          down vote










          up vote
          0
          down vote









          Sorry I don't have time rn to properly look all through your code for you but one quick thing I noticed is that it would be much nicer to put your colours in a dictionary. It could be implemented like:



          colours = {"black": (  0,  0,  0),
          "white": (255,255,255),
          ...}


          And then to get the colours you can just call colours["black"] rather than black which ties them all up together in one data structure which makes your code easier to understand and neater. Also, if you want to use your colors list which you currently have to iterate through (I don't know if you do), then you can use the new colours dictionary with .index()






          share|improve this answer












          Sorry I don't have time rn to properly look all through your code for you but one quick thing I noticed is that it would be much nicer to put your colours in a dictionary. It could be implemented like:



          colours = {"black": (  0,  0,  0),
          "white": (255,255,255),
          ...}


          And then to get the colours you can just call colours["black"] rather than black which ties them all up together in one data structure which makes your code easier to understand and neater. Also, if you want to use your colors list which you currently have to iterate through (I don't know if you do), then you can use the new colours dictionary with .index()







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 27 at 19:24









          13ros27

          247114




          247114












          • Instead of building a table, just call pygame.Color('black').
            – Gareth Rees
            Nov 28 at 10:20










          • @GarethRees Does pygame have colours hard coded into it? I didn't know that
            – 13ros27
            Nov 28 at 10:58


















          • Instead of building a table, just call pygame.Color('black').
            – Gareth Rees
            Nov 28 at 10:20










          • @GarethRees Does pygame have colours hard coded into it? I didn't know that
            – 13ros27
            Nov 28 at 10:58
















          Instead of building a table, just call pygame.Color('black').
          – Gareth Rees
          Nov 28 at 10:20




          Instead of building a table, just call pygame.Color('black').
          – Gareth Rees
          Nov 28 at 10:20












          @GarethRees Does pygame have colours hard coded into it? I didn't know that
          – 13ros27
          Nov 28 at 10:58




          @GarethRees Does pygame have colours hard coded into it? I didn't know that
          – 13ros27
          Nov 28 at 10:58


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207284%2fpaint-program-made-in-pygame%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Список кардиналов, возведённых папой римским Каликстом III

          Deduzione

          Mysql.sock missing - “Can't connect to local MySQL server through socket”