Snažím se vytvořit základní pixel art editor, a v současné době bojuje těžké najít chybu v mém kódu. Tužka funguje dobře, ale guma není.
Hlavní soubor
import pygame as pg
from settings import *
from gui import *
from canvas import Canvas
from utils import Tools
from os import path
import sys
class Pyxel:
def __init__(self, tile_size: int):
pg.init()
# Main screen
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
self.screen_rect = self.screen.get_rect()
#
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
pg.key.set_repeat(500, 100)
self.load_data()
self.tile_size = tile_size
def load_data(self):
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, 'img')
font_folder = path.join(game_folder, 'fonts')
self.pencil_icon = pg.image.load(path.join(img_folder, PENCIL_ICON)).convert_alpha()
self.pencil_icon_hover = pg.image.load(path.join(img_folder, PENCIL_HOVER)).convert_alpha()
self.pencil_icon_clicked = pg.image.load(path.join(img_folder, PENCIL_CLICKED)).convert_alpha()
self.eraser_icon = pg.image.load(path.join(img_folder, ERASER_ICON)).convert_alpha()
self.eraser_icon_hover = pg.image.load(path.join(img_folder, ERASER_HOVER)).convert_alpha()
self.eraser_icon_clicked = pg.image.load(path.join(img_folder, ERASER_CLICKED)).convert_alpha()
def new(self):
""" Initialize variables and do the initial setup """
self.canvas = Canvas(self)
self.gui = GUI(self)
self.tool_buttons = pg.sprite.Group()
self.load_toolbtns()
self.selected_tool = self.pencil_tool
self.selected_tool.clicked = True
self.selected_color = BLACK
self.canvas_grid = []
for x in range(0, self.tile_size):
self.canvas_grid.append([])
for y in range(0, self.tile_size):
self.canvas_grid[x].append({"color":None, "status":False})
def load_toolbtns(self):
self.pencil_tool = ToolBtns(self, self.pencil_icon, self.pencil_icon_hover, self.pencil_icon_clicked, 5, 5, Tools.pencil)
self.erase_tool = ToolBtns(self, self.eraser_icon, self.eraser_icon_hover, self.eraser_icon_clicked, 42, 5, Tools.eraser)
def run(self):
""" App loop """
self.running = True
while self.running:
self.dt = self.clock.tick(FPS)
self.draw()
self.events()
def quit(self):
pg.quit()
sys.exit()
def events(self):
# catch all the events here
for event in pg.event.get():
mouse_state = pg.mouse.get_pressed()
x, y = self.get_tile()
if event.type == pg.QUIT:
self.quit()
elif event.type == pg.MOUSEBUTTONDOWN:
# event.button = (left, middle, right, wheel up, wheel down)
if event.button == 1 and self.selected_tool.tool_type == Tools.pencil:
print(x, y)
self.register_pixel(x, y, draw=True)
elif event.button == 1 and self.selected_tool.tool_type == Tools.eraser:
print(x, y)
self.register_pixel(x, y, draw=False)
elif event.type == pg.MOUSEMOTION:
# mouse_state = Bool(left, middle, right)
if mouse_state[0] and self.selected_tool.tool_type == Tools.pencil:
print(x, y)
self.register_pixel(x, y, draw=True)
if mouse_state[0] and self.selected_tool.tool_type == Tools.eraser:
print(x, y)
self.register_pixel(x, y, draw=False)
for btn in self.tool_buttons:
if btn.rect.collidepoint(pg.mouse.get_pos()):
btn.hovered = True
if mouse_state[0]:
if btn.tool_type != self.selected_tool.tool_type:
self.selected_tool.clicked = False
self.selected_tool = btn
self.selected_tool.clicked = True
else:
btn.hovered = False
def get_tile(self):
""" Returns the tile that the mouse is pointing """
x, y = pg.mouse.get_pos()
x -= self.canvas.rect.x
y -= self.canvas.rect.y
dx = x // self.tile_size
dy = y // self.tile_size
if (dx >= 0 and dy >= 0) and (dx <= self.tile_size - 1 and dy <= self.tile_size - 1):
return (dx, dy)
return None, None
def register_pixel(self, x, y, draw=True):
""" Register new pixels onto the canvas matrix
Default color is BLACK """
if x is None or y is None:
return
try:
self.canvas_grid[x][y] = {"color":self.selected_color, "status":True} if draw else {"color":None, "status":False}
except IndexError:
print("Index Error: out of canvas range")
def draw(self):
self.screen.fill(SCREEN_COLOR)
self.canvas.draw()
self.canvas.draw_pixel(self.canvas_grid)
self.gui.draw()
self.tool_buttons.update()
self.tool_buttons.draw(self.screen)
pg.display.flip()
if __name__ == "__main__":
tile_size = int(input("Tile Size >> "))
p = Pyxel(tile_size)
p.new()
while True:
p.run()
Plátno souboru
import pygame as pg
from settings import *
class Canvas:
def __init__(self, app):
self.app = app
self.screen = app.screen
self.image = pg.Surface((CANVAS_WIDTH, CANVAS_HEIGHT))
self.image.fill(WHITE)
self.rect = self.image.get_rect(center = app.screen_rect.center)
def draw(self):
self.screen.blit(self.image, self.rect)
self.grid()
def grid(self):
for x in range(0, CANVAS_WIDTH, TILESIZE):
pg.draw.line(self.image, LIGHTGREY, (x, 0), (x, self.rect.bottom))
for y in range(0, CANVAS_HEIGHT, TILESIZE):
pg.draw.line(self.image, LIGHTGREY, (0, y), (self.rect.right, y))
def draw_pixel(self, canvas_data):
""" Draws pixels on the screen """
for row, data in enumerate(canvas_data):
for col, px in enumerate(data):
if px:
if px["status"]:
#self.canvas.image.fill(px["color"], pg.Rect(row * self.tile_size, col * self.tile_size, self.tile_size, self.tile_size))
pg.draw.rect(self.image, px["color"], pg.Rect(row * self.app.tile_size, col * self.app.tile_size, self.app.tile_size, self.app.tile_size))
GUI soubor
import pygame as pg
from settings import *
from utils import Tools
class GUI:
""" This is just the background, for now """
def __init__(self, app):
self.app = app
self.screen = app.screen
self.image = pg.Surface((GUI_WIDTH, GUI_HEIGHT))
self.image.fill(GUI_COLOR)
self.rect = self.image.get_rect()
#TODO
def add_btn(self):
pass
def draw(self):
self.screen.blit(self.image, self.rect)
class ToolBtns(pg.sprite.Sprite):
def __init__(self, app, base, hover_image, clicked_image, x, y, tool_type: Tools):
self.groups = app.tool_buttons
pg.sprite.Sprite.__init__(self, self.groups)
self.gui_surface = app.gui.image
self.gui_rect = app.gui.rect
self.screen = app.screen
self.base_image = base
self.hover_image = hover_image
self.clicked_image = clicked_image
self.tool_type = tool_type
self.x = x
self.y = y
self.image = self.base_image
self.rect = self.base_image.get_rect()
self.rect.x = self.gui_rect.x + x
self.rect.y = self.gui_rect.y + y
self.hovered = False
self.clicked = False
def update(self):
if self.hovered:
self.image = self.hover_image
if self.clicked:
self.image = self.clicked_image
else:
self.image = self.base_image
self.rect = self.image.get_rect()
self.rect.x = self.gui_rect.x + self.x
self.rect.y = self.gui_rect.y + self.y
Nástroje souboru
from enum import Enum, auto
class Tools(Enum):
pencil = auto()
eraser = auto()
Nastavení souboru
# (R, G, B)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARKGREY = (129, 129, 129)
LIGHTGREY = (190, 190, 190)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
TITLE = "Pyx"
WIDTH = 1400
HEIGHT = 1400
TILESIZE = 32
FPS = 60
GUI_WIDTH = 100
GUI_HEIGHT = 100
GUI_COLOR = (222, 222, 222)
CANVAS_WIDTH = TILESIZE * TILESIZE
CANVAS_HEIGHT = TILESIZE * TILESIZE
CANVAS_X = int(WIDTH * 0.5)
CANVAS_Y = int(HEIGHT * 0.5)
SCREEN_COLOR = DARKGREY
GRID_COLOR = LIGHTGREY
# ICONS
PENCIL_ICON = 'pencil_icon.png'
PENCIL_HOVER = 'pencil_icon_hover.png'
PENCIL_CLICKED = 'pencil_icon_clicked.png'
ERASER_ICON = 'eraser_icon.png'
ERASER_HOVER = 'eraser_icon_hover.png'
ERASER_CLICKED = 'eraser_icon_clicked.png'
Jen jsem vyslán můj kód, protože To je rozdělena do několika souborů a nechci, aby chybět nic důležitého. Doufám, že to není příliš mnoho.
Díky