jueves, 19 de marzo de 2020

MEDICION MULTIPLE DE TIEMPOS EN PYTHON 3



Imagina que tienes varias estaciones donde se procesa algún tipo de  pieza y quieres medir cuanto tiempo permanece cada pieza en cada estación. Supongamos también que cada estación tiene su tiempo de ciclo (unas inician antes otras despues, otras al mismo tiempo).

En el siguiente programa muestro como medir una cantidad determinada de tiempos con un solo temporizador y un solo hilo (thread). El programa calcula minutos, segundos y centésimas de segundo de cada estacion. La interfase de usuario es dinámica, es decir, se puede determinar el numero de registros de tiempo que se deseen (yo puse 20 pero lo puedes cambiar la variable numtimers al numero que quieras), el programa genera los elementos nesesarios. Este programa apenas consume CPU (se podría usar en un Raspberry Pi).El programa inicia el conteo al presionar un botón correspondiente, cuando se presiona de nuevo detiene el conteo de tiempo y lo guarda en un archivo de texto.

Programa en windows

El programa tiene comentarios en la mayoría de las líneas de código para que se pueda entender lo que sucede.

AVISO: Cada temporizador crea un archivo de texto, si pones 50, se crean 50 archivos de texto en el lugar donde esta el script. Se recomienda correrlo dentro de su propio folder.

Este programa fue probado con python 3.7. También nesecitas wxpython para correr este programa.

import wx
from   time                 import sleep
from   threading            import Thread
from   wx.lib.scrolledpanel import ScrolledPanel

class TimeReg():   #clase para registrar conteo de tiempo y estado del conteo.
    def __init__(self):
        self.tiempo = [0,0,0]  #registro de minutos, segundos y decasegundos
        self.activado = False  #iniciar desactivado

    def update(self):
        self.tiempo[2] += 1         # incremento cada centisegundo (1/100)

        if (self.tiempo[2] >= 100): # Han pasado 100 centisegundos (1 segundo)?
            self.tiempo[2] =  0     # resetear cuenta de centisegundos.
            self.tiempo[1] += 1     # incrementar segundero
                
        if (self.tiempo[1] >= 60):  # Han pasado 60 segundos (1 minuto)?
            self.tiempo[0] += 1     # incrementar minutero
            self.tiempo[1] =  0     # resetear segundero

    def reset(self):
        self.tiempo = [0, 0, 0]     # 0 minutos, 0 segundos, 0 centisegundos
        

class MyForm(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Timers")#llamar a la funcion constructor de Frame
        #---------------------------------------------------
        #                 VARIABLES
        #---------------------------------------------------
        self.numtimers       = 20        # cuantos timers quieres 20, 50, 200?
        self.CronometrArr    = []        # Arreglo de registro de tiempos
        self.TimeRegePattern = '{0:02d}:{1:02d}:{2:02d}'       # Darle formato al tiempo
        self.thread          = Thread(target = self.WrkrThred) # poner funcion WrkrThred en un hilo
        self.thread.daemon   = True      # Poner hilo en modo daemon, si el programa termina, tambien el hilo
        self.exitFlag        = False     # Bandera para parar el ciclo del hilo
        self.LblTimeRegeArr  = []        # Arreglo de etiquetas de tiempos
        self.BtnTimeRegrArr  = []        # Arreglo de botones.
        FntLblTimeRegrs      = wx.Font(16, wx.MODERN, wx.NORMAL, wx.BOLD)  #fuente a usar
        self.TiempoStr       = ""

        for i in range(self.numtimers):
            self.CronometrArr.append(TimeReg()) #Arreglo de objetos de registro de tiempos

        self.createFiles() #crear los archivos de texto nesesarios
        #---------------------------------------------------
        #                 WIDGETS
        #---------------------------------------------------
        PnlTimeRegpos = ScrolledPanel(self) #Panel de lecturas de tiempos
        
        for i in range(self.numtimers):
            # crear etiquetas y llenar arreglo
            self.LblTimeRegeArr.append(wx.StaticText(PnlTimeRegpos,
                                                -1,
                                                style = wx.ALIGN_CENTER|wx.ST_ELLIPSIZE_MIDDLE))
            self.LblTimeRegeArr[i].SetFont(FntLblTimeRegrs)     #asignar fuente
            self.LblTimeRegeArr[i].SetBackgroundColour('black') #asignar color de fondo
            self.LblTimeRegeArr[i].SetForegroundColour('white') #asignar color de letra
            self.LblTimeRegeArr[i].SetLabel("00:00:00")         #texto inicial

            # crear botones y llenar arreglo de botones
            self.BtnTimeRegrArr.append(wx.ToggleButton(PnlTimeRegpos ,
                                                  -1,
                                                  str(i),
                                                  size=(50,40)))
        #--------------------------------------------------
        #                 BINDS
        #--------------------------------------------------
        self.Bind(wx.EVT_CLOSE, self.onClosed) # trabajo de limpieza al salir de la aplicacion
        
        for i in range(self.numtimers):
            # poner bind a todos los botones a self.onTggleBtn
            self.BtnTimeRegrArr[i].Bind(wx.EVT_TOGGLEBUTTON,self.onTggleBtn)
 
        #---------------------------------------------------
        #                 MAIN LAYOUT
        #--------------------------------------------------
        MainSizer = wx.BoxSizer(wx.VERTICAL)
        #--------- Panel de lecturas ------
        TimeRegposSizrArr = [] #Arreglo de sizers
        PnlTimeRegposSizr = wx.BoxSizer(wx.VERTICAL)
        
        for i in range(self.numtimers):
            TimeRegposSizrArr.append(wx.BoxSizer(wx.HORIZONTAL))
            TimeRegposSizrArr[i].Add(self.BtnTimeRegrArr[i],0,wx.EXPAND|wx.ALL,2)
            TimeRegposSizrArr[i].Add(self.LblTimeRegeArr[i],1,wx.EXPAND|wx.ALL,2)
            PnlTimeRegposSizr.Add(TimeRegposSizrArr[i],1,wx.EXPAND|wx.ALL,0)
        PnlTimeRegpos.SetSizer(PnlTimeRegposSizr)
        PnlTimeRegpos.SetupScrolling() #calcular espacio para scroll bar
        #--------------------------------------------------
        MainSizer.Add(PnlTimeRegpos, 1,wx.EXPAND|wx.ALL,5)
        self.SetSizer(MainSizer)
        #---------------------------------------------------
        #            INICIAR HILOS AL ULTIMO
        #---------------------------------------------------         
        self.thread.start()       # Iniciar hilo
        
    def WrkrThred(self):
        while True: #loop "infinito"
            if self.exitFlag == False: # programa sigue corriendo?
                sleep(0.01)            # reterdo de 1 centi sedundo
                for i in range(self.numtimers):                    #iterar en los registros
                    if self.CronometrArr[i].activado == True:      #si esta activado
                        self.CronometrArr[i].update()              #actualizar registro i
                        self.TiempoStr = self.TimeRegePattern.format(self.CronometrArr[i].tiempo[0],
                                                                     self.CronometrArr[i].tiempo[1],
                                                                     self.CronometrArr[i].tiempo[2]) #Dar formato a los tiempos
                        wx.CallAfter(self.LblTimeRegeArr[i].SetLabel,self.TiempoStr)                 #Actualizar Texto correspondiente
                        #wx.callafter es un modo seguro de actualizar GUI desde un hilo.
            else:
                break #Romper el ciclo infinito

    def onTggleBtn(self, evt):
        BTN      = evt.GetEventObject()  #obtener el objeto boton que emite la señal
        BtnID    = BTN.GetLabelText()    #obtener el texto del boton
        BtnIdInt = int(BtnID)            #convertir el texto del boton en entero.
        if BTN.GetValue():
            self.CronometrArr[BtnIdInt].reset()          #reestablecer conteo
            self.CronometrArr[BtnIdInt].activado = True  #iniciar conteo
        else:
            self.savetimefile(BtnID)                     #Guardar conteo
            self.CronometrArr[BtnIdInt].activado = False #detener conteo

    def savetimefile(self,timer):
        f = open(timer+".txt", "a+")
        f.write(self.TiempoStr+"\n")
        f.close()
        
    def createFiles(self):
        for x in range(self.numtimers):
            open(str(x)+".txt","w+").close()

    def onClosed(self, evt):
        self.exitFlag = True #Salir del ciclo while del hilo
        self.Destroy()       #Destruir TODO

if __name__ == "__main__":
    app = wx.App()
    frame = MyForm().Show()
    app.MainLoop()

No hay comentarios.:

Publicar un comentario