Python: регулятор цветов экрана с использованием черепах

Я новичок в Python и пытаюсь использовать модули Turtle. В настоящее время я пытаюсь кодировать 3 ползунка (R, G, B) для настройки цветов экрана черепахи. Тем не менее, я обнаружил, что части моего кода заполнены избыточностями, не мог бы кто-нибудь сказать мне, как обрезать код, чтобы сделать его более компактным и понятным, особенно в частях draw_red, draw_green, draw_blue? Заранее спасибо.

import turtle

turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)

colors = ['red', 'green', 'blue']
painters = []
red_x, green_x, blue_x = 1, 2, 3
turtle.bgcolor(0,0,0)
def spawningPainters():
    global painter
    for i in range(len(colors)):
        painter = turtle.Turtle()
        painters.append(painter)
        painter.up()
        painter.speed(0)
        painter.setheading(90)
        painter.color(colors[i])
        painter.pencolor('black')
        painter.shape('turtle')
        painter.shapesize(5, 5, 5)
        xPos = i + 1
        painter.goto(xPos, 0)

def assign_painters():
    global redPainter, greenPainter, bluePainter
    redPainter = painters[0]
    greenPainter = painters[1]
    bluePainter = painters[2]

def draw_red(x, y):
    redPainter.ondrag(None)
    x = red_x
    redPainter.goto(x, y)
    redPainter.ondrag(draw_red)
    update_screen_color()

def draw_green(x, y):
    greenPainter.ondrag(None)
    x = green_x
    greenPainter.goto(x, y)
    greenPainter.ondrag(draw_green)
    update_screen_color()

def draw_blue(x, y):
    bluePainter.ondrag(None)
    x = blue_x
    bluePainter.goto(x, y)
    bluePainter.ondrag(draw_blue)
    update_screen_color()

def update_screen_color():
    red = max(min(redPainter.ycor(), 255) , 0)
    green = max(min(greenPainter.ycor(), 255), 0)
    blue = max(min(bluePainter.ycor(), 255), 0)

    turtle.bgcolor(int(red), int(green), int(blue))

def back_to_origin():
    for painterIndex in range(len(painters)):
        painters[painterIndex].goto(painterIndex + 1, 0)

def listening_input():
    redPainter.ondrag(draw_red)
    greenPainter.ondrag(draw_green)
    bluePainter.ondrag(draw_blue)
    turtle.onkeypress(back_to_origin, 'c')

def main():
    spawningPainters()
    assign_painters()
    listening_input()

main()
turtle.listen()
turtle.tracer(True)
turtle.mainloop()

2 ответа
2

Рассмотрение

Мне не удалось придумать способ сделать код более компактным, и, на мой взгляд, код довольно понятен. Однако у меня есть и другие отзывы.

Используйте правильный основной

Вместо:

turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)
turtle.bgcolor(0,0,0)

...

def main():
    spawningPainters()
    assign_painters()
    listening_input()

main()
turtle.listen()
turtle.tracer(True)
turtle.mainloop()

Делать:

...

if __name__ == "__main__":
    turtle.setworldcoordinates(0, 0, 4, 255)
    turtle.colormode(255)
    turtle.tracer(False)
    turtle.bgcolor(0,0,0)

    spawningPainters()
    assign_painters()
    listening_input()

    turtle.listen()
    turtle.tracer(True)
    turtle.mainloop()

Для получения дополнительной информации вы можете посмотреть этот пост: https://stackoverflow.com/questions/419163/what-does-if-name-main-do

Не позволяйте черепахе выходить за окно

С текущим кодом можно перетащить черепаху за пределы окна. Если вы затем прекратите перетаскивание, вы не сможете вернуть его, пока не сбросите положение. Чтобы этого не произошло, мы можем ограничить границы y внутри функций рисования.

def draw_blue(x, y):
    bluePainter.ondrag(None)
    x = blue_x
    y = min(y, 255)
    y = max(y, 0)
    bluePainter.goto(x, y)
    bluePainter.ondrag(draw_blue)
    update_screen_color()

Делая это, min а также max звонки могут быть удалены из update_screen_color функция. Однако вы можете оставить их как форму защитного программирования на случай, если вы добавите дополнительные функции.

def update_screen_color():
    red = redPainter.ycor()
    green = greenPainter.ycor()
    blue = bluePainter.ycor()

    turtle.bgcolor(int(red), int(green), int(blue))

Обновить цвет экрана при возврате к исходной точке

Когда положение возвращается в исходное положение, цвет экрана не перерисовывается. Если это не предусмотрено, было бы хорошо позвонить в update_screen_color функция в конце back_to_origin функция.

Несколько советов

В global painter переменная не используется нигде за пределами spawningPainters функция, поэтому ее можно удалить.

Переместите colors список в spawningPainters функция, поскольку она больше нигде не используется.

Вы можете захотеть переименовать spawningPainters к spawn_painters.

Дополнительный

Добавьте набор текста в painters список

Когда объявлен пустой список, у intellisense вашей IDE могут возникнуть проблемы с определением типа элементов, которые будет содержать список. Добавляя аннотации типов, вы можете помочь intellisense лучше понять ваш код. Вместо:

painters = []

Делать:

from typing import List

...

painters: List[turtle.Turtle] = []

    РЕДАКТИРОВАТЬ: ответ оказался довольно длинным, и я не хочу, чтобы вы забыли, что вы действительно хорошо поработали с этой программой и, особенно, признав, что, вероятно, есть лучшая альтернатива, чем утроение кода, по одной части на цвет.

    Это мой вариант менее повторяющейся версии одного и того же кода. Обратите внимание, что мои предложения не обязательно самый лучший, и вы, и я, и другие люди, которые в конечном итоге вмешаются, могли бы разработать еще лучшую программу 🙂

    Забота о государстве

    У вас была правильная идея: вам нужен контейнер со всеми черепахами, поэтому вам не нужно иметь переменную для каждой из них, но выполнить это сложно, потому что все взаимосвязано, и вам нужно, чтобы многие из ваших функций знали сразу много вещей.

    На первой итерации я пытался перепроектировать ваш код, сохраняя три рисовальщика в словаре, который затем передавался бы нескольким функциям, но в итоге я использовал ООП (объектно-ориентированное программирование), потому что это тип ситуации, когда это пригодится.

    Мы создаем объекты, которые связаны и связаны, и позволяем им хранить свои собственные отношения и т. Д.

    Обратите внимание, что это не Только способ сделать это.

    Ваша собственная черепаха

    Во-первых, нам нужны наши черепахи, которые знают о себе, чтобы их .ondrag могут знать о своем текущем положении и т. д.

    import turtle
    
    class Painter(turtle.Turtle):
        def __init__(self, turtle_app, i, colour):
            super().__init__()
            self.turtle_app = turtle_app
            self.up()
            self.speed(0)
            self.setheading(90)
            self.color(colour)
            self.pencolor('black')
            self.shape('turtle')
            self.shapesize(5, 5, 5)
            self.goto(i, 0)
            self.ondrag(self.draw)
    
        def draw(self, x, y):
            x = self.xcor()
            self.goto(x, y)
            self.turtle_app.update_screen_colour()
    

    Обратите внимание, что __init__ метод — это то, что инициализирует черепаху и делает почти все, что у вас было в вашем spawningPainters метод. Разница в том, что Painter сам определяет свой draw метод, и что он хранит ссылку на этот turtle_app который является «главным контроллером» вашего приложения:

    class TurtleApp:
        def __init__(self):
            self.spawn_painters()
    
        def spawn_painters(self):
            self.painters = {}
            for i, colour in enumerate(["red", "green", "blue"], start=1):
                self.painters[colour[0]] = Painter(self, i, colour)
    
        def update_screen_colour(self):
                r = max(min(self.painters["r"].ycor(), 255) , 0)
                g = max(min(self.painters["g"].ycor(), 255), 0)
                b = max(min(self.painters["b"].ycor(), 255), 0)
    
                turtle.bgcolor(int(r), int(g), int(b))
    
        def reset(self):
            for painter in self.painters.values():
                painter.goto(painter.xcor(), 0)
            self.update_screen_colour()
    

    В TurtleApp объект хранит художников в словаре, индексируемом по первой букве цвета, и предоставляет вспомогательные методы, которые вам понадобились для сброса позиций, обновления цвета экрана и т. д.

    Используя «правильный основной», например, предложенный другой ответ (и это хорошее предложение!), Ваш код заканчивается на

    if __name__ == "__main__":
        turtle.setworldcoordinates(0, 0, 4, 255)
        turtle.colormode(255)
        turtle.tracer(False)
        turtle.bgcolor(0,0,0)
    
        ta = TurtleApp()
        turtle.onkeypress(ta.reset, 'c')
    
        turtle.listen()
        turtle.tracer(True)
        turtle.mainloop()
    

    Обратите внимание на две строки, которые я разделил, где вы создаете свой TurtleApp а потом скажи turtle позвонить в TurtleApp.reset если пользователь нажимает "c" ключ. Это было проверено и, похоже, работает так же, как и ваше оригинальное.

    Удалите нерелевантные переменные

    Постарайтесь сделать свой код максимально самодостаточным. У вас есть специальные переменные для хранения x положение каждого рисовальщика, и это прекрасно, но обратите внимание, что как только вы инициализируете его, вы всегда можете запросить его x положение с painter.xcor(), что вы знаете, потому что вы сделали то же самое для y с участием painter.ycor() при обновлении цвета фона.

    Теперь, если вы знаете x положение вашего маляра, вы всегда можете предотвратить его перемещение влево или вправо, сделав что-то вроде painter.goto(painter.xcor(), y) в draw функции, вам не нужно оставлять x_red, x_blue, а также x_green переменные с вами 🙂

    Перечислить

    enumerate (который я использовал в for цикл для инициализации художников) очень полезен, когда вам нужно перебрать набор значений (в моем случае, цвета), а также узнать их индекс (который вы используете для установки x положение в начале). Если ты не знаешь enumerate, вы можете прочитать об этом в документации или в этот учебник. Обратите внимание, что я также использовал часто упускаемый из виду start необязательный аргумент, чтобы сообщить enumerate начать отсчет с 1 вместо 0.

    Я сделал еще одну вещь:

    Соглашения об именах PEP 8

    Python обычно предпочитает snake_case имена, поэтому все мои функции имеют регистр, отличный от ваших исходных.

    Окончательное предложение по кодексу

    Теперь все вместе:

    import turtle
    
    class Painter(turtle.Turtle):
        def __init__(self, turtle_app, i, colour):
            super().__init__()
            self.turtle_app = turtle_app
            self.up()
            self.speed(0)
            self.setheading(90)
            self.color(colour)
            self.pencolor('black')
            self.shape('turtle')
            self.shapesize(5, 5, 5)
            self.goto(i, 0)
            self.ondrag(self.draw)
    
        def draw(self, x, y):
            x = self.xcor()
            self.goto(x, y)
            self.turtle_app.update_screen_colour()
    
    class TurtleApp:
        def __init__(self):
            self.spawn_painters()
    
        def spawn_painters(self):
            self.painters = {}
            for i, colour in enumerate(["red", "green", "blue"], start=1):
                self.painters[colour[0]] = Painter(self, i, colour)
    
        def update_screen_colour(self):
                r = max(min(self.painters["r"].ycor(), 255) , 0)
                g = max(min(self.painters["g"].ycor(), 255), 0)
                b = max(min(self.painters["b"].ycor(), 255), 0)
    
                turtle.bgcolor(int(r), int(g), int(b))
    
        def reset(self):
            for painter in self.painters.values():
                painter.goto(painter.xcor(), 0)
            self.update_screen_colour()
            
    
    if __name__ == "__main__":
        turtle.setworldcoordinates(0, 0, 4, 255)
        turtle.colormode(255)
        turtle.tracer(False)
        turtle.bgcolor(0,0,0)
        ta = TurtleApp()
        turtle.onkeypress(ta.reset, 'c')
        turtle.listen()
        turtle.tracer(True)
        turtle.mainloop()
    ```
    

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *