Python procédural en POO
desir_indecis -
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
-
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.
-
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.
-
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.