pygame color choice window with button reaction

It's a bit of code, and I have a feeling there are some massive shortcuts to the way I've done it,
I'm thinking pygame.rect.get_collide()  instead of all the math and conversions I did to get the cursor location over my " + "  and " - "  boxes, But I can clean it all up tomorrow.
I still need to get the buttons all working in proper order {ON, NEW, OFF, COLOR}
add a sizing tool to size the circles
and saving the image may be way beyond my scope, but I'll attempt it.
I'm starting to think WxWidgets would be better for this... more menu stuff already set up to go and interact.
But seeing it through!

Update: 6-4-18
Cleaned up a lot, and made an engine to handle updates outside of the main loop.
There was a ton that got fixed.
 

Wanted to share the progress!

Video:




and Code:

#!usr/bin/python3
# -*- coding: utf-8 -*-

import pygame as pg
from sys import exit




pg.init()

class SkullGlobals(object):
    """ class container for globally used variables """
    def __init__(self):
        self.WIDTH = 800
        self.HEIGHT = 800
        self.CENTER_SCREEN_X = 0 + self.WIDTH//12
        self.CENTER_SCREEN_Y = 0 + self.HEIGHT//12
        self.CENTER_SCREEN_H = self.HEIGHT - self.HEIGHT//6
        self.CENTER_SCREEN_W = self.WIDTH - self.WIDTH//6
        self.background_color = (110, 100, 110)
        self.BLUE = (40, 60, 200)
        self.LBLUE = (100, 100, 255)
        self.BORDER = (self.WIDTH + self.HEIGHT)//200
        self.left_y = 0 + self.HEIGHT//100
        self.left_x = 0 + self.WIDTH//100
        self.right_y = self.HEIGHT - self.left_y
        self.right_x = self.WIDTH - self.left_x
        self.small_height = self.left_y * 5
        self.small_width = self.left_x * 10
        self.left_upper_y = 0 + (self.HEIGHT//10)
        self.left_upper_x = 0 + (self.WIDTH//10)
        self.right_lower_x = self.WIDTH - (self.WIDTH//10)
        self.right_lower_y = self.HEIGHT - (self.HEIGHT//10)
        self.CENTER = (self.WIDTH//2, self.HEIGHT//2)
        self.FULL_SIZE = (self.WIDTH//4 + self.HEIGHT//4)//2
        self.SMALL_SIZE = (self.WIDTH//50 + self.HEIGHT//50)//2
        self.RED = (230, 50, 50)
        self.GREEN = (50, 230, 50)
        self.D_WHITE = (200,200,200)
        self.base_color = 150
        self.menu_bar_color = (90, 100, 80)


## can not do this --> self.screen = pg.display.set_mode((globals.WIDTH, globals.HEIGHT))

globals = SkullGlobals()
screen = pg.display.set_mode((globals.WIDTH, globals.HEIGHT))
pg.display.set_caption(('My Painter'))
screen.fill(globals.background_color)
animation_timer = pg.time.Clock()
pg.display.flip()

class DrawSurface(object):
    def __init__(self):
        self.width = globals.CENTER_SCREEN_W
        self.height = globals.CENTER_SCREEN_H
        self.x_pos = globals.CENTER_SCREEN_X
        self.y_pos = globals.CENTER_SCREEN_Y

    def draw_surface(self):
        w = int(self.width)
        h = int(self.height)
        center_screen = pg.Surface((w, h))
        center_screen.fill(globals.D_WHITE)
        screen.blit(center_screen, (self.x_pos, self.y_pos))

    def get_placement(self):
        end_x = self.x_pos + self.width
        end_y = self.y_pos + self.height
        position_size = [self.x_pos, self.y_pos, end_x, end_y]
        return position_size

class WindowRect(object):
    def __init__(self, screen, pos_x, pos_y, width, height):
        self.screen = screen
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.width = width
        self.height = height
        self.color = (130, 160, 130)
        self.thickness = globals.BORDER

    def draw_rect(self):
        size = (self.pos_x, self.pos_y, self.width, self.height)
        border_size = (self.pos_x - 3, self.pos_y - 3, self.width + 5, self.height + 5)
        pg.draw.rect(self.screen, globals.menu_bar_color, border_size, 5 )
        pg.draw.rect(self.screen, self.color, size, 0)

    def get_dimensions(self):
        """ return the needed dimensions for use """
        end_x_pos = self.pos_x + self.width
        end_y_pos = self.pos_y + self.height
        rect_list_dims = [self.pos_x, self.pos_y, end_x_pos, end_y_pos]
        return rect_list_dims

class ButtonXY(object):
    """Class for drawing rectangle,  and getting position of rectangle"""
    def __init__(self, my_screen, pos_x, pos_y, rect_width, rect_height, R, G, B, name):

        self.my_screen = my_screen
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.width = rect_width
        self.height = rect_height
        self.R = R
        self.G = G
        self.B = B
        self.name = name
        self.font = pg.font.SysFont('Arial', self.height//3)

    def place_rect(self):
        """ pygame.draw.rect(screen, color, (x, y, width, height), width of outline) """
        thick = 5
        color = (self.R, self.G, self.B)
        light_color = (140, 130, 150)
        size = (self.pos_x, self.pos_y, self.width, self.height)
        inner_box = (self.pos_x + 3, self.pos_y + 3, self.width - 6, self.height - 6)
        center_height = self.height//4
        center_width = self.width//4
        text_pos_x = self.pos_x + center_width
        text_pos_y = self.pos_y + center_height
        #text_size = self.font.size(self.name)
        # ADJUSTING center position by TEXT size will require a lot of error proofing...
        pg.draw.rect(self.my_screen, color, size, 5)
        pg.draw.rect(self.my_screen, light_color, inner_box, 0)
        self.my_screen.blit(self.font.render(self.name, True, (0, 0, 0)), (text_pos_x, text_pos_y))

    def get_rect_place(self):
        """ return the needed dimensions for button click check """
        end_x_pos = self.pos_x + self.width
        end_y_pos = self.pos_y + self.height
        rect_list_dims = [self.pos_x, self.pos_y, end_x_pos, end_y_pos]
        return rect_list_dims



class CircleXY(object):
    """ Class for drawing circle to screen """
    def __init__(self,my_screen, pos_x, pos_y, radius, R, G, B, speed, thickness):
        self.my_screen = my_screen
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.radius = radius
        self.R = R
        self.G = G
        self.B = B
        self.x_change = speed
        self.y_change = speed
        self.thickness = thickness

    def draw_circle(self):
        """ place a circle at given position with attributes given """
        color = (self.R, self.G, self.B)
        position = (self.pos_x, self.pos_y)
        pg.draw.circle(self.my_screen, color, position, self.radius, self.thickness)

class DrawCircle(object):
    """ Used to place a circle at given x, y, with radius, color upon use
         ** Speed and Thickness may be employed later """
    def __init__(self, my_screen, pos_x, pos_y, radius, color, speed=None, thickness=0):
        self.my_screen = my_screen
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.radius = radius
        self.color = color
        self.speed = speed
        self.thickness = thickness

    def add_circle(self):
        position = (self.pos_x, self.pos_y)
        pg.draw.circle(self.my_screen, self.color, position, self.radius, self.thickness)

class ColorPicker(object):
    """
        Color change graphic to allow change of color of circles
        Making all dimensions dependant on globals will help to scale the 

        whole thing later
    """

    def __init__(self):
        self.width = globals.WIDTH//5
        self.height = globals.HEIGHT//5
        self.x_pos = globals.WIDTH - self.width - globals.BORDER
        self.y_pos = globals.HEIGHT - (self.height * 2) - globals.BORDER
        self.window = WindowRect(screen, self.x_pos, self.y_pos, self.width, self.height)
        self.font = pg.font.SysFont('Arial', self.height//5)

    def start_rect(self):
        """ Graphical parts of color picker  rect(surface, color, size, thick)"""
        self.window.draw_rect()
        size_w = self.width//5
        size_h = self.height//5
        diff_x = (self.x_pos + self.width) - size_w
        diff_y = (self.y_pos + self.height) - size_h
        red = (255, 0, 0)
        green = (0, 255, 0)
        blue = (0, 0, 255)
        light = (200, 200, 200)
        dark = (0, 0, 0)
        upper_R = (self.x_pos, self.y_pos, size_w, size_h)
        lower_R = (diff_x, self.y_pos, size_w, size_h)
        upper_L = (self.x_pos, diff_y, size_w, size_h)
        lower_L = (diff_x, diff_y, size_w, size_h)
        half_lower_L = (diff_x, diff_y, size_w//2, size_h)
        pg.draw.rect(screen, red, upper_R, 0)         #B
        pg.draw.rect(screen, green, lower_R, 0)       #D
        pg.draw.rect(screen, blue, upper_L, 0)        #A
        pg.draw.rect(screen, light, lower_L, 0)       #C
        pg.draw.rect(screen, dark, half_lower_L, 0)


    def color_buttons(self):
        """Buttons to go below, left, right, above color choices
            red_box = (self.x_pos, self.y_pos, size_w, size_h)
            green_box = (diff_x, self.y_pos, size_w, size_h)
            blue_box = (self.x_pos, diff_y, size_w, size_h)
            shade_box = (diff_x, diff_y, size_w, size_h)
        """

        size_w = self.width//5
        size_h = self.height//5
        diff_x = (self.x_pos + self.width) - size_w
        diff_y = (self.y_pos + self.height) - size_h
        border = 2
        red = (255, 0, 0)
        blue = (0, 0, 255)
        grey = (75, 75, 75)
        green = (0, 255, 0)
        black = (0, 0, 0)
        plus = " + "
        plus = self.font.render(plus, True, black)
        minus = " - "
        minus = self.font.render(minus, True, black)

        # make the boxes locations dependant on the start_rect() color boxes

        green_left = (diff_x - size_w - border, self.y_pos, size_w, size_h)
        green_down = (diff_x, self.y_pos + size_h + border, size_w, size_h)
        shade_left = (diff_x - size_w - border, diff_y, size_w, size_h)
        shade_up = (diff_x, diff_y - size_h - border, size_w, size_h)
        red_right = (self.x_pos + size_w + border, self.y_pos, size_w, size_h)
        red_down = (self.x_pos, self.y_pos + size_h + border, size_w, size_h)
        blue_up = (self.x_pos, diff_y - size_h - border, size_w, size_h)
        blue_right = (self.x_pos + size_w + border, diff_y, size_w, size_h)

        pg.draw.rect(screen, blue, blue_right, 3)
        screen.blit(minus, (self.x_pos + size_w, diff_y))
        pg.draw.rect(screen, blue, blue_up, 3)
        screen.blit(plus, (self.x_pos, diff_y - size_h))
        pg.draw.rect(screen, red, red_down, 3)
        screen.blit(minus, (self.x_pos, self.y_pos + size_h))
        pg.draw.rect(screen, red, red_right, 3)
        screen.blit(plus, (self.x_pos + size_w, self.y_pos))
        pg.draw.rect(screen, green, green_left, 3)
        #print("green_left =", green_left)
        screen.blit(plus, (diff_x - size_w, self.y_pos))
        pg.draw.rect(screen, green, green_down, 3)
        screen.blit(minus, (diff_x, self.y_pos + size_h))
        pg.draw.rect(screen, grey, shade_left, 3)
        screen.blit(minus, (diff_x - size_w, diff_y))
        pg.draw.rect(screen, grey, shade_up, 3)
        screen.blit(plus, (diff_x, diff_y - size_h))


    def get_button_place(self, which_button):
        """
        From the button locations defined in color_buttons()
        return the coordinates needed to detect mouse click in button
        """

        size_w = self.width//5
        size_h = self.height//5
        diff_x = (self.x_pos + self.width) - size_w
        diff_y = (self.y_pos + self.height) - size_h
        border = 2
        # Making the variables less mathy to return
        # basically we need to return the (x, y, x+width, y+width)
        GL_x = diff_x - size_w - border
        GL_y = self.y_pos
        GD_y = self.y_pos + size_h + border
        BU_y = diff_y - size_h - border
        BR_x = self.x_pos + size_w + border
        RR_x = self.x_pos + size_w + border
        RD_y = self.y_pos + size_h + border
        SHL_x = diff_x - size_w - border
        SHU_y = diff_y - size_h - border



        green_left = (GL_x, self.y_pos, GL_x + size_w, self.y_pos + size_h)
        green_down = (diff_x, GD_y, diff_x + size_w, GD_y + size_h)
        shade_left = (SHL_x, diff_y, SHL_x + size_w, diff_y + size_h)
        shade_up = (diff_x, SHU_y - border, diff_x + size_w, diff_y + size_h)
        red_right = (RR_x, self.y_pos, RR_x + size_w, self.y_pos + size_h)
        red_down = (self.x_pos, RD_y, self.x_pos + size_w, RD_y + size_h)
        blue_up = (self.x_pos, BU_y, self.x_pos + size_w, BU_y + size_h)
        blue_right = (BR_x, diff_y, BR_x + size_w, diff_y + size_h)

        if which_button == "plus red":
            return red_right
        elif which_button == "minus red":
            return red_down
        elif which_button == "plus green":
            #print("plus green=", green_left)
            return green_left
        elif which_button == "minus green":
            return green_down
        elif which_button == "plus shade":
            return shade_up
        elif which_button == "minus shade":
            return shade_left
        elif which_button == "plus blue":
            return blue_up
        elif which_button == "minus blue":
            return blue_right

        else:
            pass




    def run(self, R, G, B):
        """ create draw the color picker window graphics """
        radius = self.width//5
        diff_x = self.x_pos + self.width
        diff_y = self.y_pos + self.height
        pos_x = (self.x_pos + diff_x)//2
        pos_y = (self.y_pos + diff_y)//2
        position = (pos_x, pos_y)
        color = (R, G, B)
        self.start_rect()
        pg.draw.circle(screen, color, position, radius, 0)
        self.color_buttons()


class StateEngine(object):
    """ Class container for running engine, screen, updates """
    def __init__(self):
        self.on_button = ButtonXY(screen, globals.left_x, globals.left_y, globals.small_width, globals.small_height, globals.base_color - 100, globals.base_color, globals.base_color - 100, "ON")

        self.new_circle_button = ButtonXY(screen, globals.left_x + globals.small_width + 8, globals.left_y, globals.small_width, globals.small_height, globals.base_color - 100, globals.base_color, globals.base_color - 100, "NEW")

        self.off_button = ButtonXY(screen, (globals.right_x - globals.small_width), (globals.right_y - globals.small_height) , globals.small_width, globals.small_height, globals.base_color, globals.base_color - 100, globals.base_color -100, "OFF")

        self.color_button = ButtonXY(screen, (globals.right_x - globals.small_width * 2 - 8), (globals.right_y - globals.small_height), globals.small_width, globals.small_height, globals.base_color, globals.base_color -100, globals.base_color - 100, "COLOR")

        self.rect_list = [self.on_button, self.off_button, self.new_circle_button, self.color_button]

        self.ON = CircleXY(screen, globals.left_upper_x, globals.left_upper_y, globals.SMALL_SIZE,  50, 230, 50, 1, 0)

        self.OFF = CircleXY(screen, globals.right_lower_x, globals.right_lower_y, globals.SMALL_SIZE, 200, 50, 50, 1, 0)

        self.object_list = [self.ON, self.OFF]
        self.running = True
        self.center_surface = DrawSurface()
        self.drawn_circs = []
        self.dsp = self.center_surface.get_placement()
        self.windows = []
        self.new_window = ColorPicker()
        self.red = 0
        self.green = 0
        self.blue = 0

    def screen_update(self):
        self.center_surface.draw_surface()
        pg.draw.rect(screen, globals.menu_bar_color, (0, 0, globals.WIDTH, globals.HEIGHT//20), 0)

        pg.draw.rect(screen, globals.menu_bar_color, (0, globals.HEIGHT - globals.HEIGHT//20, globals.WIDTH, globals.HEIGHT//20), 0)

        for rects in self.rect_list:
            rects.place_rect()
        for circs in self.drawn_circs:
            circs.add_circle()
        for window in self.windows:
            window.run(self.red, self.green, self.blue)



    def update_buttons(self, position, on, off, new, color_pick):
        for rects in self.rect_list:
            cbd = rects.get_rect_place()
            if cbd[0] < position[0] < cbd[2]:
                if cbd[1] < position[1] < cbd[3]:
                    #print("IN BUTTON", rects.name)
                    if rects.name == "ON":
                        on = True
                        off = False
                    if rects.name == "OFF":
                        off = True
                        on = False
                        color_pick = False
                        if len(self.windows) > 0:
                            self.windows.pop()
                    if rects.name == "NEW":
                        new = True
                        on = True
                        color_pick = False
                    if rects.name == 'COLOR':
                        on = True
                        new = False
                        color_pick = True
                        off = False
                        new_window = ColorPicker()
                        self.windows.append(new_window)

        return on, off, new, color_pick

    def update_draws(self, on, off, new, color_pick):
        if on and not off:
            if new and not color_pick:
                radius = 20
                (x, y) = pg.mouse.get_pos()
                # dsp = Drawn Screen Parameters
                limit_x_left = self.dsp[0] - radius
                limit_x_right = self.dsp[2] - radius
                limit_y_top = self.dsp[1] - radius
                limit_y_bottom = self.dsp[3] - radius

                if limit_x_left < x < limit_x_right:
                    if limit_y_top < y < limit_y_bottom:

                        color = (self.red, self.green, self.blue)

                        circle = DrawCircle(screen, x, y, radius, color)
                        self.drawn_circs.append(circle)

    def check_color_picker(self, position, on, off, new, color_pick):
        if on and color_pick:
            if not off and not new:
                (gx, gy, gdx, gdy) = self.new_window.get_button_place("plus green")
                (g2x, g2y, g2dx, g2dy) = self.new_window.get_button_place("minus green")

                (bx, by, bdx, bdy) = self.new_window.get_button_place("plus blue")
                (b2x, b2y, b2dx, b2dy) = self.new_window.get_button_place("minus blue")

                (rx, ry, rdx, rdy) = self.new_window.get_button_place("plus red")
                (r2x, r2y, r2dx, r2dy) = self.new_window.get_button_place("minus red")
                (shx, shy, shdx, shdy) = self.new_window.get_button_place("plus shade")

                (sh2x, sh2y, sh2dx, sh2dy) = self.new_window.get_button_place("minus shade")

                if gx < position[0] < gdx and gy < position[1] < gdy:
                    if 0 <= self.green <= 250:
                        self.green += 5
                elif g2x < position[0] < g2dx and g2y < position[1] < g2dy:
                    if 255 >= self.green >= 5:
                        self.green -= 5
                elif bx < position[0] < bdx and by < position[1] < bdy:
                    if 0 <= self.blue <= 250:
                        self.blue += 5
                elif b2x < position[0] < b2dx and b2y < position[1] < b2dy:
                    if 255 >= self.blue >= 5:
                        self.blue -= 5
                elif rx < position[0] < rdx and ry < position[1] < rdy:
                    if 0 <= self.red <= 250:
                        self.red += 5
                elif r2x < position[0] < r2dx and r2y < position[1] < r2dy:
                    if 255 >= self.red >= 5:
                        self.red -= 5
                elif shx < position[0] < shdx and shy < position[1] < shdy:
                    if 0 <= self.red <= 250:
                        self.red += 5
                    if 0 <= self.green <= 250:
                        self.green += 5
                    if 0 <= self.blue <= 250:
                        self.blue += 5
                elif sh2x < position[0] < sh2dx and sh2y < position[1] < sh2dy:
                    if 255 >= self.red >= 5:
                        self.red -= 5
                    if 255 >= self.green >= 5:
                        self.green -= 5
                    if 255 >= self.blue >= 5:
                        self.blue -= 5

class DrawIt(object):
    """ class container for defining pygame screen run and iteraction """
    def __init__(self):
        self.engine = StateEngine()
        self.running = True
    def run_draw(self):
        on = False
        off = False
        new = False
        color_pick = False
        screen.fill(globals.background_color)
        while self.running:
            animation_timer.tick(60)
            screen.fill(globals.background_color)
            self.engine.screen_update()
            events = pg.event.get()
            for event in events:
                if event.type == pg.QUIT:
                    self.running = False
                    pg.quit()
                    exit(0)
                if event.type == pg.MOUSEBUTTONDOWN:
                    position = pg.mouse.get_pos()
                    on, off, new, color_pick = self.engine.update_buttons(position, on, off, new, color_pick)

                    self.engine.update_draws(on, off, new, color_pick)
                    self.engine.check_color_picker(position, on, off, new, color_pick)

            if on:
                item = self.engine.object_list[0]
                item.draw_circle()

            if off:
                item = self.engine.object_list[1]
                item.draw_circle()
     pg.display.flip()




drawing = DrawIt()
drawing.run_draw()




Comments

Popular posts from this blog

playing with color in powershell python

JavaScript Ascii animation with while loops and console.log

playing with trigonometry sin in pygame