Elenco di campi in named tuple

Introduzione

Con le conoscenze acquisite, possiamo procedere a un refactoring del codice scritto precedentemente, in modo da migliorare alcuni aspetti della gestione della "tabella di dati".

Gli obiettivi che ci proponiamo sono:

  • avere la possibilità di definire i diversi campi di cui un elemento della tabella è composto (come se si trattasse di un record in Pascal o di una struct in C);

  • poter accedere al valore del campo o per nome o per posizione;

  • far sì che i vari record vengano inseriti all'interno di una lista.

Per realizzare questi obiettivi, ci converrà partire definendo una struttura dati per la memorizzazione di quello che ci serve per un "campo". Per iniziare, memorizzeremo la classe associata (il "tipo"), l'etichetta, la dimensione (per questioni di formattazione), il tipo di allineamento. A tale scopo definiremo una apposita classe, Field.

Successivamente, dovremo memorizzare i campi del nostro elemento (ossia quelli che abbiamo visto si chiamano slots) in una struttura dati un po' più complessa di una tupla, in modo da avere la possibilità di accedere ad essi sia attraverso un indice numerico, sia attraverso una chiave. Per questo motivo, useremo una struttura dati apposita, chiamata namedtuple, che fa parte del modulo collections.

La classe Field

La classe Field è molto semplice. Ci consente di memorizzare le informazioni necessarie per gestire un campo del nostro elemento:

class Field():
    def __init__(self, class_constraint, label='',  size=20, alignment='left'):
        self.__class_constraint = class_constraint
        self.__label = label
        self.__size = size
        self.__alignment = alignment

    def getClassConstraint(self):
        return self.__class_constraint
    def getLabel(self):
        return self.__label
    def getSize(self):
        return self.__size
    def getAlignment(self):
        return self.__alignment
    def getMethod(self):
        return self.__method

    def setLabel(self, v):
        self.__label = v
        return self

La classe Picture

La ridefinizione della classe Picture si basa sul fatto che i dati relativi agli slot vengono memorizzati in una namedtuple. Inoltre, è stato aggiunto un metodo di classe per ottenere l'elenco dei campi, in modo da poterli visualizzare nell'intestazione quando i dati di più elementi vengono presentati in forma tabellare (vedi codice di PictureBox).

from basic_io import *
from PosInt import *
from Field import *
from Date import *

import collections

class Picture(object):
    nt = collections.namedtuple('fields', 'filename, width, height, filetype, quality, date_taken')

    __fields = nt(
        Field(str, 'nome del file', 30),
        Field(PosInt, 'larghezza', 8, 'right'),
        Field(PosInt, 'altezza', 8, 'right'),
        Field(str, 'tipo di file', 5),
        Field(float, 'qualità immagine', 4, 'right'),
        Field(Date, 'data di ripresa', 8, 'right')
        )

    __slots__ = nt._fields

    def __setattr__(self, name, value):
        assert(isinstance(value, getattr(self.__fields, name).getClassConstraint()))
        super().__setattr__(name, value)

    def getAttrByName(self, name):
        return self.__getattribute__(name)

    def getAttrByPosition(self, position):
        name = self.__slots__[position]
        return self.getAttrByName(name)

    def inputData(self):
        print("*** INSERIMENTO DATI PER UNA IMMAGINE ***")
        for fieldname, field in zip(self.__slots__, self.__fields):
            self.inputField(fieldname, field)

    def inputField(self, fieldname, field):
        message = 'Inserisci un valore per "%s": ' % field.getLabel()
        data=checked_input(message, field.getClassConstraint())
        self.__setattr__(fieldname, data)

    def outputData(self):
        print('*** DATI DI UNA IMMAGINE ***')
        for fieldname, field in zip(self.__slots__, self.__fields):
            self.outputField(fieldname, field)

    def outputField(self, fieldname, field):
        print(field.getLabel(),': ', self.getAttrByName(fieldname))

    @classmethod
    # un metodo "di classe" riceve come primo parametro un riferimento
    # alla classe
    def getFields(cls):
        return cls.__fields

La classe PictureBox

Nella classe PictureBox abbiamo solo aggiunto il codice per la visualizzazione dell'intestazione, nel quale si richiama un metodo statico della classe Picture.

class PictureBox(list):
    def showHeader(self):
        fields=list()
        # creiamo una lista dove mettere le etichette dei campi
        for field in Picture.getFields():
            fields.append(field.getLabel())
            # aggiungiamo un'etichetta alla lista
        print(*fields, sep=' | ')
        # visualizziamo tutte le etichette della lista
        # *fields trasforma l'oggetto fields in un elenco, come se
        # scrivessimo
        # print(fields[0], fields[1], fields[2], ...)

    def showPictures(self):
        for i in range(len(self)):
            self[i].outputData()

L'interazione con l'utente avviene come nel seguente esempio:

Quante immagini vuoi inserire? 2
*** INSERIMENTO DATI PER UNA IMMAGINE ***
Inserisci un valore per "nome del file": gatto.jpg
Inserisci un valore per "larghezza": 300
Inserisci un valore per "altezza": 200
Inserisci un valore per "tipo di file": JPEG
Inserisci un valore per "qualità immagine": f
Sembra che tu non abbia inserito un valore valido…
Inserisci un valore per "qualità immagine": 0.9
Inserisci un valore per "data di ripresa": 20/100/2009
Sembra che tu non abbia inserito un valore valido…
Inserisci un valore per "data di ripresa": 20/10/2010
*** INSERIMENTO DATI PER UNA IMMAGINE ***
Inserisci un valore per "nome del file": cane.png
Inserisci un valore per "larghezza": 40
Inserisci un valore per "altezza": 40
Inserisci un valore per "tipo di file": PNG
Inserisci un valore per "qualità immagine": 1
Inserisci un valore per "data di ripresa": 12/10/2010
*** DATI DI UNA IMMAGINE ***
nome del file:  gatto.jpg
larghezza:  300
altezza:  200
tipo di file:  JPEG
qualità immagine:  0.9
data di ripresa:  20/10/2010
*** DATI DI UNA IMMAGINE ***
nome del file:  cane.png
larghezza:  40
altezza:  40
tipo di file:  PNG
qualità immagine:  1.0
data di ripresa:  12/10/2010

results matching ""

    No results matching ""