Python procédural en POO

Bonjour, 

J'ai un code python écrit en procédural je veux le convertir en orienté objet. Est-ce que quelqu'un peut m'aider ?

import sys, pygame

pygame.init()
BLACK=0,0,0
BLUE=0,0,255
GREEN=0,255,0
WHITE=255,255,255
size = width, height = 660, 460
FRAMES_PER_SECOND = 10 
clock = pygame.time.Clock() 
screen = pygame.display.set_mode(size)
def draw_Barre_verticale(left,h):
    pygame.draw.rect(screen, BLUE, pygame.Rect(left, 0, 10, h))

def draw_Barre_horizontale(top,w):
    pygame.draw.rect(screen, BLUE, pygame.Rect(0, top, w, 10))
def draw_chariot(X,Y, Ref):

    pygame.draw.rect(screen, GREEN, pygame.Rect(X-20+5, Y-20+5, 40, 40))
    font = pygame.font.SysFont("arial", 10)
    texte_x = pygame.font.Font.render(font, Ref, True, (0,0,0))
    screen.blit(texte_x, dest=(X, Y))
def trouver_case_vide():
    for j in range(5):
        for i in range(5):
            if LL[j][i]=='':
                return i,j
    #si aucune vide
    return None,None

def retour_pos_init():
    global X, Y, deltaX, deltaY
    if X>X0:
        X=X-deltaX
        if X<X0: X=X0
    if Y<Y0:
        Y=Y-deltaY
        if Y>Y0: Y=Y0
def draw_magasin():
    for j in range(5):
        for i in range(5):
            pygame.draw.rect(screen, WHITE, pygame.Rect(Xmin+i*120, Ymin+j*80, 120, 80),width=2)
            if LL[j][i]!='':
                font = pygame.font.SysFont("arial", 10)
                texte_x = pygame.font.Font.render(font, LL[j][i], True, (0,255,255))
                screen.blit(texte_x, dest=(Xmin+i*120+5, Ymin+(4-j)*80+5))
                
def aller_pos_desiree(Xd,Yd):
    global X, Y, deltaX, deltaY
    if X < Xd  :
            X=X+deltaX
            if X > Xd: X=Xd
        
    if Y > Yd :
            Y=Y+deltaY
            if Y < Yd: Y=Yd
    
LL=[]   # liste de listes contenant les ref des produits stockés
NC,NL=5,5       #magasin de 5 colonnes 5 lignes
for i in range(NC):
    L=[]
    for j in range(NL):
        L.append('') # au debut magasin vide aucun stock donc Ref ''
    LL.append(L)

#on initialise certaines cases avec des refs avec des refs
for i in range(5):
    for j in range(3):
        LL[i][j]="Ref"+str(i)+str(j)
for i in range(4):
    LL[i][3]="Ref"+str(i)+str(j)
for i in range(4):
    LL[i][4]="Ref"+str(i)+str(j)
# dans le magasin on a mnt des cases vides et des cases pleines
#limits de deplacement des barres
Xmin=40;Xmax=width  # barre verti
Ymin=0; Ymax=height-40  #barre horizontale
#vitesse de deplac: 2 pixels par 1/FRAMES_PER_SECOND =1/10sec
deltaX=2;deltaY=-2
#position de repos
X0,Y0=20,height-20  #barre vert à X0=20 et barre horiz à Y0=height-20
#init a la pos de repos des barres 
X=X0;Y=Y0

#trouver une case vide qui sera désignée par NumC,NumL numéros de colonne et de ligne
NumC, NumL=trouver_case_vide() #si ca return None aucune case vide
print(NumC,NumL)


#affichage en position initiale
draw_Barre_verticale(X0,height)
draw_Barre_horizontale(Y0,width)
pygame.display.update()
clock.tick(FRAMES_PER_SECOND)
#position desiree
#NumC entre 0 et 4 et NumL entre 0 et 4
#NumC=2; NumL=3
Xdesiree=NumC*120+Xmin+60
Ydesiree=(4-NumL)*80+Ymin+40 # NumL=0 en bas de l'ecran
compt=0 #compteur de temps

#au debut on fait un chargement d'un produit de Ref XR123 par exemple
#puis on fait un déchargement du meme produit
#et le cycle recommence
chargement=True  # tru --> chargement , false --> dechargement
Ref="XR123" # Ref contiendra la reference du produit à charger ou qui sera déchargé
Chariot=Ref #Chariot contient la ref du produit dans le chariot
while True:
    #print(LL)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()
    #print(X,Y)
    if compt==0: #compt=0 debut du demi cycle on fait un chargement ou dechargement
        if chargement: Chariot=Ref  #chariot contient le produit
        else: Chariot="" #retour chariot vide
        aller_pos_desiree(Xdesiree,Ydesiree)
        
    if (X,Y) ==(Xdesiree, Ydesiree): #arrivee a pos desiree
        compt=compt+1 #temporisation par comptage
        #print(compt)
        if chargement and compt==1: #chargement
            LL[NumL][NumC]=Chariot ; Chariot='' #placement du produit à la case
        if not chargement and compt==1:#dechargement
            Chariot=LL[NumL][NumC]; LL[NumL][NumC]='' #retrait du produit de la case
        
    if compt==50: #fin de temporisation 50*1/FRAMES_PER_SECOND =50/10=5s
        #après 5sec retour à pos initiale
        retour_pos_init()
        if X==X0  and Y==Y0 :
            compt=-50
            if chargement: chargement=False #baseculment du chargement/dechargement
            else: chargement=True
    if compt<0:
        compt=compt+1 #tempo par comptage de -50 à 0
        #print(compt)
    
    screen.fill(BLACK) 
    draw_magasin()
    draw_Barre_verticale(X, height)
    draw_Barre_horizontale(Y,width)
    draw_chariot(X,Y,Chariot)
    pygame.display.update()
    clock.tick(FRAMES_PER_SECOND)

3 réponses

  1. D'après toi, quels sont les objets que tu manipules?
    Quels sont leurs attributs et/ou propriétés?

    Pourquoi des variables globales?

    C'est déjà un début de réflexion.

    1. Ceci ne répond pas à ta question, mais en tant que "one-liner" invétéré, je n'ai pas pu résister.

      Voici ton initialisation en simplifié:

      NC,NL=5,5       #magasin de 5 colonnes 5 lignes
      LL = [[""] * NC for _ in range(NL)]                                                                                   

      Si vraiment seules les deux dernière colonnes de la dernière ligne sont vides, on peut faire plus simple.

      1. Un exemple amélioré de ton code lequel si j'ai bien saisi représente un magasin automatique à la manière du stockage d'un entrepôt.

        import pygame as pg
        import math
        import random
        
        FPS = 60
        SCREEN_WIDTH = 660
        SCREEN_HEIGHT = 460
        SCREEN_COLOR = pg.Color(0, 0, 0, 0)
        
        AREA_BG_COLOR = pg.Color(0, 0, 0, 0)
        AREA_BORDER_COLOR = pg.Color(0, 255, 0, 0)
        
        AREA_ADRESS_COLOR = pg.Color(0, 255, 255, 0)
        AREA_PRODUCT_COLOR = pg.Color(255, 255, 0, 0)
        AREA_QUANTITY_COLOR = pg.Color(0, 255, 255, 0)
        
        CART_BAR_SIZE = 20
        CART_BAR_BG_COLOR = pg.Color(0, 0, 255, 160)
        CART_BAR_FG_COLOR = pg.Color(128, 128, 255, 255)
        
        CURSOR_SIZE = CART_BAR_SIZE * 2
        CURSOR_BG_COLOR = pg.Color(0, 0, 128)
        CURSOR_FG_COLOR = pg.Color(128, 128, 128)
        # CART_CURSOR_SPEED = 4
        CART_CURSOR_SPEED = 12
        # CART_TRANSFERT_SPEED = 5
        CART_TRANSFERT_SPEED = 15
        CART_WAIT_DELAY = 25
        
        
        class Point:
            __slots__ = ('x', 'y')
        
            def __init__(self, x=0, y=0):
                self.x = x
                self.y = y
        
            def __str__(self):
                return f'x: {self.x}, y: {self.y}'
        
        
        class Area(pg.sprite.Sprite):
            width = 120
            height = 80
        
            def __init__(self, x, y, adress):
                super().__init__()
                self.__adress = adress
                self.image = pg.Surface((self.width, self.height)).convert()
                self.rect = self.image.get_rect()
                self.image.fill(AREA_BORDER_COLOR)
                pg.draw.rect(self.image, AREA_BG_COLOR, self.rect.inflate(-2, -2))
                self.font = pg.font.SysFont('mono', 15)
                self.image.blit(
                    self.font.render(self.__adress, True, AREA_ADRESS_COLOR), (3, 3)
                )
                self.rect.topleft = x, y
                self._product = None
                self._quantity = 0
                self._rect_product = pg.Rect(
                    5, self.rect.height / 3, self.width - 10, self.height / 3 - 4
                )
                self._rect_quantity = pg.Rect(
                    5, self.rect.h / 3 * 2, self.rect.w - 10, self.rect.h / 3 - 4
                )
        
            def _draw_product(self):
                self.image.fill(AREA_BG_COLOR, self._rect_product)
                text_surf = self.font.render(self._product, True, AREA_PRODUCT_COLOR)
                self.image.blit(text_surf, self._rect_product)
        
            def _draw_quantity(self):
                self.image.fill(AREA_BG_COLOR, self._rect_quantity)
                text_surf = self.font.render(
                    str(self._quantity), True, AREA_QUANTITY_COLOR
                )
                self.image.blit(text_surf, self._rect_quantity)
        
            @property
            def adress(self):
                return self.adress
        
            @property
            def product(self):
                return self._product
        
            @property
            def quantity(self):
                return self._quantity
        
            def put(self, product, quantity):
                assert self._quantity == 0, "Can't change product, stock is not to 0"
                self._product = product
                self._quantity = quantity
                self._draw_product()
                self._draw_quantity()
        
            def pick(self, quantity):
                assert (
                    self._quantity >= quantity
                ), f"Can't pick more than {self._quantity} units"
                self._quantity -= quantity
                self._draw_quantity()
        
            def add(self, quantity):
                # assert (
                    # self._quantity + quantity > self._quantity_max
                # ), f"Can't contains more than {self._quantity_max} units"
                self._quantity += quantity
                self._draw_quantity()
        
        
        class CartBar(pg.sprite.Sprite):
            def __init__(self, x, y, width, height):
                super().__init__()
                self.image = pg.Surface((width, height), pg.SRCALPHA).convert_alpha()
                self.rect = self.image.get_rect()
                self.image.fill(CART_BAR_BG_COLOR)
                self.rect.topleft = x, y
                self.font = pg.font.SysFont('mono', 15, True)
        
        
        class HorizontalCartBar(CartBar):
            def __init__(self):
                y = SCREEN_HEIGHT - CURSOR_SIZE + CART_BAR_SIZE / 2
                super().__init__(0, y, SCREEN_WIDTH, CART_BAR_SIZE)
                self._label = ""
        
            def label(self, text):
                if self._label != text:
                    self._label = text
                    self.image.fill(CART_BAR_BG_COLOR)
                    text = self.font.render(text.upper(), True, CART_BAR_FG_COLOR)
                    rect = text.get_rect()
                    y = self.rect.h / 2
                    for x in (CURSOR_SIZE + 50, SCREEN_WIDTH - 50 - rect.w):
                        self.image.blit(text, text.get_rect(midleft=(x, y)))
        
        
        class VerticalCartBar(CartBar):
            def __init__(self):
                x = (CURSOR_SIZE - CART_BAR_SIZE) / 2
                super().__init__(x, 0, CART_BAR_SIZE, SCREEN_HEIGHT)
                self._label = ""
        
            def label(self, text):
                if self._label != text:
                    self._label = text
                    self.image.fill(CART_BAR_BG_COLOR)
                    x = self.rect.w / 2
                    text = self.font.render(text.upper(), True, CART_BAR_FG_COLOR)
                    text = pg.transform.rotate(text, -90)
                    rect = text.get_rect()
                    for y in (
                        CURSOR_SIZE + rect.h / 2,
                        SCREEN_HEIGHT - CURSOR_SIZE - rect.h,
                    ):
                        self.image.blit(text, text.get_rect(center=(x, y)))
        
        
        class CartCursor(pg.sprite.Sprite):
            def __init__(self):
                super().__init__()
                self.image = pg.Surface(
                    (CURSOR_SIZE, CURSOR_SIZE), pg.SRCALPHA
                ).convert()
                self.rect = self.image.get_rect()
                self.image.fill(CURSOR_BG_COLOR)
                self.rect.topleft = 0, SCREEN_HEIGHT - CURSOR_SIZE
                self.font = pg.font.SysFont('mono', 15, True)
        
            def quantity(self, quantity):
                self.image.fill(CURSOR_BG_COLOR)
                text = self.font.render(str(quantity), True, CURSOR_FG_COLOR)
                center = self.rect.w / 2, self.rect.h / 2
                self.image.blit(text, text.get_rect(center=center))
        
        
        # Forklift
        class Cart:
            def __init__(self, store, *groups):
                self.store = store
                self.hbar = HorizontalCartBar()
                self.vbar = VerticalCartBar()
                self.cursor = CartCursor()
                for group in groups:
                    group.add(self.hbar, self.vbar, self.cursor)
                self._moving = False
                self._action = 0
        
            def _set_route(self):
                def get_points(start, end, step):
                    dist = math.sqrt((start.x - end.x) ** 2 + (start.y - end.y) ** 2)
                    numbers = int(dist / step)
                    d = Point((end.x - start.x) / numbers, (end.y - start.y) / numbers)
                    points = [
                        Point(start.x + d.x * i, start.y + d.y * i)
                        for i in range(numbers)
                    ]
                    points.append(end)
                    return points
        
                dest = Point(*self.store.get_pos(self._product))
                self._route = get_points(
                    self.cursor.rect, dest, CART_CURSOR_SPEED * (30 / FPS)
                )
        
            def _set_work(self, work, product, quantity):
                self._moving = True
                self._work = work
                self._product = product
                self._quantity = quantity
                self._i = 0
                self._action = 1
                self._set_route()
                self.cursor.quantity(self._quantity)
                self.hbar.label(self._product)
                self.vbar.label(self._work.__name__)
        
            def pick(self, product, quantity):
                if self._moving:
                    raise RuntimeWarning("Can't pick product, cart is moving")
                self._set_work(self.store.pick, product, quantity)
        
            def put(self, product, quantity):
                if self._moving:
                    raise RuntimeWarning("Can't put product, cart is moving")
                self._set_work(self.store.put, product, quantity)
        
            def update(self):
                if self._action in (1, 4):
                    try:
                        point = self._route[self._i]
                        self.cursor.rect.topleft = point.x, point.y
                        self.hbar.rect.y = point.y + 10
                        self.vbar.rect.x = point.x + 10
                        self._i += 1
                    except IndexError:
                        if self._action == 1:
                            self._i = 0
                            self._action = 2
                        else:
                            self._action = 0
                            self._moving = False
                elif self._action == 2:
                    self._i += 1
                    if not self._i % int(CART_TRANSFERT_SPEED * (30 / FPS)):
                        self._work(self._product, 1)
                        self._quantity -= 1
                        self.cursor.quantity(self._quantity)
                        if not self._quantity:
                            self._action = 3
                            self._i = 0
                            self._route.reverse()
                elif self._action == 3:
                    # délai d'attente
                    self._i += 1
                    if self._i == int(25 / (30 / FPS)):
                        self._i = 0
                        self._action = 4
        
            @property
            def moving(self):
                return self._moving
        
        
        # Warehouse ?
        class Store:
            def __init__(self, width, height, x, y, *groups):
                self.groups = groups
                self._stock = tuple(
                    Area(
                        x + i * (Area.width + 3),
                        y + j * (Area.height + 3),
                        f'{chr(65 + j)}{i + 1}',
                    )
                    for j in range(height)
                    for i in range(width)
                )
                for group in groups:
                    group.add(self._stock)
        
            def pick(self, product, quantity):
                for area in self._stock:
                    if area.product == product:
                        try:
                            area.pick(quantity)
                        except AssertionError:
                            raise ValueError()
                        return
                raise ValueError(f"product {product} not in stock")
        
            def put(self, product, quantity):
                for area in self._stock:
                    if area.product == product:
                        area.add(quantity)
                        return
                for area in self._stock:
                    if area.product is None:
                        area.put(product, quantity)
                        return
                for area in self._stock:
                    if area.quantity == 0:
                        area.put(product, quantity)
                        return
                raise ValueError(f"Can't add product in stock, no free area")
        
            @property
            def products(self):
                return tuple(area.product for area in self._stock)
        
            def get_quantity(self, product):
                for area in self._stock:
                    if area.product == product:
                        return area.quantity
                raise ValueError(f"product {product} not in store")
        
            def get_avalaible_products(self):
                return tuple(area.product for area in self._stock if area.quantity)
        
            def get_pos(self, product):
                for area in self._stock:
                    if area.product == product:
                        return area.rect.midtop
        
        
        class App:
            def __init__(self):
                pg.init()
                self.screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
                self.screen.convert()
                self.screen.fill(SCREEN_COLOR)
                pg.display.update()
                self.display_group = pg.sprite.LayeredUpdates()
                self.store = Store(5, 5, 40, 5, self.display_group)
                for i in range(1, 26):
                    quantity = random.randint(1, 20)
                    self.store.put(f'produit {i}', quantity)
                self.cart = Cart(self.store, self.display_group)
                # Tests
                self.ACTION_EVENT = pg.USEREVENT
                pg.time.set_timer(self.ACTION_EVENT, 2000)
        
            def _random_action(self):
                work = (self.cart.pick, self.cart.put)[random.randrange(2)]
                if work == self.cart.pick:
                    products = self.store.get_avalaible_products()
                    product = random.choice(products)
                    quantity = random.randint(1, min(self.store.get_quantity(product), 20))
                else:
                    products = self.store.products
                    product = random.choice(products)
                    quantity = random.randint(1, 20)
                work(product, quantity)
        
            def _clear(self, surf, rect):
                surf.fill(SCREEN_COLOR, rect)
        
            def run(self):
                clock = pg.time.Clock()
                running = True
                while running:
                    for evt in pg.event.get():
                        if evt.type == pg.QUIT:
                            running = False
                        elif evt.type == self.ACTION_EVENT:
                            if not self.cart.moving:
                                self._random_action()
                    self.cart.update()
                    self.display_group.update()
                    self.display_group.clear(self.screen, self._clear)
                    rects = self.display_group.draw(self.screen)
                    pg.display.update(rects)
                    clock.tick(FPS)
        
                pg.quit()

        Pas mal de choses peuvent être améliorées, entre autres :
        - Créer une (ou plusieurs) classe dérivée d'Exception afin de gérer les exceptions de l'application.
        - Ajouter des méthodes à la class Store, par exemple une méthode donnant les produits en pénurie, une autre donnant le produit stocké à telle adresse, etc.
        - Changer la couleur du chariot (barre, curseur) en fonction de l'action effectuée, alimentation ou prélèvement.
        - Ajouter une quantité de stockage max. dans les zones, et gérer le cas lorsqu'il est tenté d'ajouter plus de marchandises que la zone peut reçevoir.
        - Revoir la façon dont sont créées les zones, ie: s'arranger pour que ce soit plus configurable.
        - Certainement revoir le nom des classes Store et Cart, après c'est sans doute plus simple à écrire que Warehouse et Forklift...
        - Et de fil en aiguille, d'autres idées viennent, etc, etc.