import threading
import time
import tkinter.ttk
from tkinter import *
from tkinter import messagebox
from PIL import Image, ImageTk
from _datetime import date
import re
from ImpetomC import ImpetomC
from ImpetomCError import ImpetomCError
from ImpetomCElectrodeError import ImpetomCElectrodeError
from datetime import datetime
import winsound

"""
Clase que genera la interfaz gráfica de la aplicación
"""

"""
Variables globales de la aplicación
"""

arduinoImpetom = 0 # 0 indica arduinoImpetom desconectado, 1 indica que esta conectado
Reconstruir = False
mode = "Setup"
img = Image.open('imagenInicial.jpg')
sclRangeEnable = False

"""
Funciones para validación de datos de entrada
"""

def validateName():
    """
    Funcion que valida el nombre del paciente ingresado
    :return: 0 si existe algun error, 1 si el nombre es correcto (el campo lblNameError no esta vacio)
    """

    ret=0
    patName = txtName.get("1.0", END)
    if len(patName) <=1 :
        lblNameError.config(text="Este campo no puede estar vacío")
    else:
        ret=1
        lblNameError.config(text="")
    return ret

def validateCI():
    """
    Funcion que valida la CI del paciente ingresado
    :return: 0 si existe algun error, 1 si la CI es correcta (el campo lblNameError no esta vacio)
    """

    ret = 0
    patCi = txtCi.get("1.0", END).strip("\n")
    lenCI=len(patCi)
    if not re.match('^[0-9]*$',patCi):
        lblCiError.config(text="La CI solo puede contener números")
    elif lenCI <= 6 or lenCI > 8:
        lblCiError.config(text="CI inválida")
    else:
        ret = 1
        lblCiError.config(text="")
    return ret

def arduinoImpetomConected():
    """
    Funcion que verifica que el dispositivoImpetomC esta conectado
    :return: 0 si existe algun error, 1 si no
    """

    ret = 1
    global arduinoImpetom
    if arduinoImpetom == 0:
        try:  # Verifico que haya un arduino conectado al equipo al iniciar y si es el arduino de ImpetomC
            ImpetomC.startUp()
            arduinoImpetom = 1  # indicador que arduino Impetom está conectado
            lblError.config(text="", bg='white')
        except ImpetomCError as e:
            lblError.config(text=str(e), bg='white')
            arduinoImpetom = 0
            ret = 0
    return ret

def validateSexoBiologico():
    """
    Funcion que valida el sexp biologico del paciente ingresado
    :return: 0 si existe algun error, 1 si no
    """

    ret = 0
    patSex = cmbBioSex.get().lower()
    if patSex == "seleccionar":
        lblBioSexError.config(text="Seleccione una opción válida")
    else:
        lblBioSexError.config(text="")
        ret = 1
    return ret

def validateBirthDate():
    """
    Funcion que valida la fecha de nacimiento del paciente ingresado
    :return: 0 si existe algun error, 1 si no
    """

    ret = 0
    txtBDate = str(txtBirthDate.get("1.0", 'end-1c'))
    try:
        datetime.strptime(txtBDate, "%d-%m-%Y")
        lblBirthDateError.config(text="")
        ret = 1
    except ValueError:
        lblBirthDateError.config(text="La fecha debe ser dd-mm-yyyy")
    finally:
        return ret

def validateMedida():
    """
    Función que valida la entrada de los datos de medida de circunferencia del tórax,
    El dato debe ser un número entre 100 y 2000mm
    """

    ret = 0
    fantomaMed = txtMedida.get("1.0", END).strip("\n")
    lenFantomaMed = len(fantomaMed)
    if not re.match('^[0-9]*$', fantomaMed):
        lblMedidaError.config(text="Este campo solo puede contener números positivos")
    elif lenFantomaMed <= 2:  # Verifico que el número sea mayor que 100 mm
        lblMedidaError.config(text="El perímetro debe ser mayor o igual a 100 mm")
    elif lenFantomaMed >= 5:
        lblMedidaError.config(text="El perímetro es demasiado grande")
    else:
        ret = 1
        lblMedidaError.config(text="")
        if lenFantomaMed == 4:
            intFantomaMed = int(fantomaMed)
            if intFantomaMed >= 2000:
                lblMedidaError.config(text="El perímetro es demasiado grande")
                ret = 0

    return ret

def validateMedidaZ():
    """
    Función que valida la altura de colocación del cinturon, debe estar entre 10 mm y 999mm
    """

    ret = 0
    fantomaMedZ = txtMedidaZ.get("1.0", END).strip("\n")
    fantomaMedZ = fantomaMedZ.strip("-")  # Para validar ints > 0 y < 0 con la misma regExp
    lenFantomaMedZ = len(fantomaMedZ)
    lblMedidaZError.place(x=(265 / 1200) * screenWidth, y=(560 / 750) * screenHeight)
    if not re.match('^[0-9]*$', fantomaMedZ):
        lblMedidaZError.config(
            text="La altura de colocación del cinturón debe ser un número entero medido desde la apófisis del esternón")
    elif lenFantomaMedZ < 1 or lenFantomaMedZ > 3:  #
        lblMedidaZError.config(text="La referencia de altura no es válida")
    else:
        ret = 1
        lblMedidaZError.config(text="")
    return ret

"""
Eventos para botones Start y Stop
"""

def btnStartClicked():
    """
    Evento del botón start
    """

    global Reconstruir, arduinoImpetom
    if validateName() +validateCI() + arduinoImpetomConected() + validateSexoBiologico() + validateBirthDate() + validateMedida() + validateMedidaZ() == 7:
        Reconstruir = True
        txtMedida.config(state='disable')
        txtMedidaZ.config(state='disable')
        txtName.config(state='disable')
        txtCi.config(state='disable')
        btnIncTime.config(state='disable')
        btnIncTime2.config(state='disable')
        btnDecTime.config(state='disable')
        btnDecTime2.config(state='disable')
        btnStart.config(state='disable')
        lblTimeError.config(text="Presione Stop para cambiar esta opción")
        btnReconstruccion.config(state='disable')
        btnSetup.config(state='disable')
        cmbModel.config(state='disable')
        lblModeMessage.config(text='Presione Stop para cambiar esta opción')
        btnCalibrar.config(state='disable')
        btnReset.config(state='disable')
        threading.Thread(target=reloadImage).start()


def btnStopClicked():
    """
    Evento del botón Stop
    """

    global Reconstruir
    Reconstruir = False
    time.sleep(4)
    txtMedida.config(state='normal')
    txtMedidaZ.config(state='normal')
    txtName.config(state='normal')
    txtCi.config(state='normal')
    btnIncTime.config(state='normal')
    btnIncTime2.config(state='normal')
    btnDecTime.config(state='normal')
    btnDecTime2.config(state='normal')
    btnStart.config(state='normal')
    btnStop.config(state='disable')
    lblTimeError.config(text="")
    lblModeMessage.config(text='')
    btnCalibrar.config(state='normal')
    btnReset.config(state='normal')
    if mode == "Setup":
        btnReconstruccion.config(state='normal')
    else:
        btnSetup.config(state='normal')
    cmbModel.config(state='readonly')

def btnResetClicked():
    """
    Evento para el botón reset
    """

    global img, photo
    lblDate.config(text="Fecha:      " + str(date.today().strftime("%b-%d-%Y")))
    btnReset.config(state='disable')
    ImpetomC.reset()
    img = Image.open('imagenInicial.jpg')
    photo = ImageTk.PhotoImage(img)
    lblImg.config(text="", image=photo, width=(700 / 1200) * screenWidth, height=(500 / 750) * screenHeight)
    cmbModel.current(0)
    lblpRangeValue.config(text=initialPValue)
    lblGananciaValue.config(text=initialGValue)
    lblVmaxValue.config(text=initialVmaxValue)
    lblVminValue.config(text=initialVminValue)
    lblSaveTimeValue.config(text=initialSaveTimeValue)
    lblError.config(text="", bg="white")
    btnSetupClicked()
    rbtnSclOptionsDisable.select()
    if tkinter.messagebox.askyesno("Reset", "Desea también eliminar los datos del paciente?"):
        txtName.delete("1.0", "end")
        txtCi.delete("1.0", "end")
        txtMedida.delete("1.0", "end")
        txtMedidaZ.delete("1.0", "end")
        txtBirthDate.delete("1.0", "end")
        cmbBioSex.current(0)

"""
Esta función se encarga de actualizar las imágenes en lblImg 
"""

def reloadImage():
    """
    Esta funcion es la encargada de actualizar las imagenes en la etiqueta lblImg
    """

    global img, photo, arduinoImpetom, Reconstruir, bpWeight, screenWidth, sclRangeEnable, mode
    ti = time.time()
    while Reconstruir:
        try:
            medida = txtMedida.get("1.0", END).strip("\n")
            medidaZ = txtMedidaZ.get("1.0", END).strip("\n")
            vmax = float(lblVmaxValue.cget("text"))
            vmin = float(lblVminValue.cget("text"))
            pvalue = round(float(lblpRangeValue.cget("text")), 2)
            frecValue = str(30000 - int(lblGananciaValue.cget("text")) * 100)
            shape = cmbModel.get().lower()
            pacBDate = str(datetime.strptime(txtBirthDate.get("1.0", 'end-1c'), "%d-%m-%Y").strftime("%Y%m%d"))
            pacSex = cmbBioSex.get()
            ImpetomC.generarImagenTomografica(vmax=vmax, screenWidth=screenWidth, sclEnable=sclRangeEnable, vmin=vmin,
                                              mode=mode, shape=shape, p=pvalue, frecuenciaDDS=frecValue)
            tf = time.time()
            elapsedTime = int(tf - ti)
            img = Image.open('IMG.jpg')
            photo = ImageTk.PhotoImage(img)
            lblImg.config(text="", image=photo)
            lblError.config(text="", bg='#2b70e4')
            if elapsedTime > int(float(lblSaveTimeValue.cget('text')) * 60):  # Conversión de minutos a segundos
                ImpetomC.guardarImagenEnHistoriaClinica(pacientName=txtName.get("1.0", END),
                                                        pacientCi=txtCi.get("1.0", END), p=pvalue, pacSex=pacSex,
                                                        pacBirthDate=pacBDate, medida=medida, shape=shape,
                                                        medidaZ=medidaZ, mode=mode)
                ti = time.time()
            if Reconstruir:
                btnStop.config(state='normal')
        except ImpetomCError as e:  # errores por problemas de conexion del dispositivo Arduino
            lblError.config(text=str(e) + " Por favor verifique la conexión y presione start nuevamente", bg='white')
            winsound.Beep(440, 500)
            Reconstruir = False
            arduinoImpetom = 0  # arduino ImpetomC no está conectado
            btnStopClicked()
            ImpetomC.guardarImagenEnHistoriaClinica(pacientName=txtName.get("1.0", END),
                                                    pacientCi=txtCi.get("1.0", END), p=pvalue, pacSex=pacSex,
                                                    pacBirthDate=pacBDate, medida=medida, shape=shape, medidaZ=medidaZ,
                                                    mode=mode)
        except ImpetomCElectrodeError as e:  # Errores detectados sobre los electrodos
            lblError.config(text=str(e), bg='white', fg='#f00')
            tf = time.time()
            elapsedTime = int(tf - ti)
            img = Image.open('IMG.jpg')
            photo = ImageTk.PhotoImage(img)
            lblImg.config(text="", image=photo)
            if e.regularGanancia and elapsedTime > int(float(lblSaveTimeValue.cget(
                    'text')) * 60):  # Para errores de regular ganancia se guarda la imágen, si son errores de falso contacto no se guarda la imágen
                ImpetomC.guardarImagenEnHistoriaClinica(pacientName=txtName.get("1.0", END),
                                                        pacientCi=txtCi.get("1.0", END), p=pvalue, pacSex=pacSex,
                                                        pacBirthDate=pacBDate, medida=medida, shape=shape,
                                                        medidaZ=medidaZ, mode=mode)
                ti = time.time()
            elif not e.regularGanancia:
                winsound.Beep(440, 500)
            if Reconstruir:
                btnStop.config(state='normal')
        except RuntimeError:  # Se esta recontruyendo la imagen y se presiona el botón de salir
            time.sleep(5)
            root.destroy()

"""
Funciones botones p del jac
"""
maxPValue = 0.9
minPValue = 0.05
def incPRange():
    """
    Evento del boton ">" de p
    """

    enableDecpRange()
    pRangeValue = float(lblpRangeValue.cget("text"))
    pRangeValue += 0.01
    if pRangeValue >= maxPValue:
        pRangeValue = maxPValue
        disbleIncpRange()
    lblpRangeValue.config(text=str(round(pRangeValue,2)))

def incPRange2():
    """
    Evento del boton ">>" de p
    """

    enableDecpRange()
    pRangeValue = float(lblpRangeValue.cget("text"))
    pRangeValue += 0.1
    if pRangeValue >= maxPValue:
        pRangeValue = maxPValue
        disbleIncpRange()
    lblpRangeValue.config(text=str(round(pRangeValue,2)))

def decPRange():
    """
    Evento del boton "<" de p
    """

    enableIncpRange()
    pRangeValue = float(lblpRangeValue.cget("text"))
    pRangeValue -= 0.01
    if pRangeValue <= minPValue:
        pRangeValue = minPValue
        disableDecpRange()
    lblpRangeValue.config(text=str(round(pRangeValue,2)))

def decPRange2():
    """
    Evento del boton "<<" de p
    """

    enableIncpRange()
    pRangeValue = float(lblpRangeValue.cget("text"))
    pRangeValue -= 0.1
    if pRangeValue <= minPValue:
        pRangeValue = minPValue
        disableDecpRange()
    lblpRangeValue.config(text=str(round(pRangeValue,2)))

def enableIncpRange():
    """
    Permite habilitar los botones ">>" y ">" de p
    """

    btnIncpRange.config(state="normal")
    btnIncpRange2.config(state="normal")

def enableDecpRange():
    """
    Permite habilitar los botones "<" y "<<" de p
    """

    btnDecpRange.config(state="normal")
    btnDecpRange2.config(state="normal")

def disbleIncpRange():
    """
    Permite deshabilitar los botones ">>" y ">" de p
    """

    btnIncpRange.config(state="disable")
    btnIncpRange2.config(state="disable")

def disableDecpRange():
    """
        Permite deshabilitar los botones "<" y "<<" de p
    """

    btnDecpRange.config(state="disable")
    btnDecpRange2.config(state="disable")

"""
Eventos de los botones para el manejo de la ganancia
"""

maxGananciaValue = 5
minGananciaValue = -20

def incGanancia():
    """
        Evento del boton ">" de la ganancia del sistema
    """

    enableDecGanancia()
    gananciaValue = int(lblGananciaValue.cget("text"))
    gananciaValue += 1
    if gananciaValue >= maxGananciaValue:
        gananciaValue = maxGananciaValue
        disableIncGanancia()
    lblGananciaValue.config(text=gananciaValue)

def incGanancia2():
    """
        Evento del boton ">>" de la ganancia del sistema
    """

    enableDecGanancia()
    gananciaValue = int(lblGananciaValue.cget("text"))
    gananciaValue += 5
    if gananciaValue >= maxGananciaValue:
        gananciaValue = maxGananciaValue
        disableIncGanancia()
    lblGananciaValue.config(text=gananciaValue)

def decGanancia():
    """
    Evento del boton "<" de la ganancia del sistema
    """

    enableIncGanancia()
    gananciaValue = int(lblGananciaValue.cget("text"))
    gananciaValue -= 1
    if gananciaValue <= minGananciaValue:
        gananciaValue =minGananciaValue
        disableDecGanancia()
    lblGananciaValue.config(text=gananciaValue)

def decGanancia2():
    """
    Evento del boton "<<" de la ganancia del sistema
    """

    enableIncGanancia()
    gananciaValue = int(lblGananciaValue.cget("text"))
    gananciaValue -= 5
    if gananciaValue <= minGananciaValue:
        gananciaValue = minGananciaValue
        disableDecGanancia()
    lblGananciaValue.config(text=gananciaValue)

def enableIncGanancia():
    """
    Permite habilitar los botones ">>" y ">" de ganancia
    """

    btnIncGanancia.config(state="normal")
    btnIncGanancia2.config(state="normal")

def enableDecGanancia():
    """
    Permite habilitar los botones "<<" y "<" de ganancia
    """

    btnDecGanancia.config(state="normal")
    btnDecGanancia2.config(state="normal")

def disableIncGanancia():
    """
    Permite deshabilitar los botones ">>" y ">" de ganancia
    """

    btnIncGanancia.config(state="disable")
    btnIncGanancia2.config(state="disable")

def disableDecGanancia():
    """
    Permite deshabilitar los botones "<<" y "<" de ganancia
    """

    btnDecGanancia.config(state="disable")
    btnDecGanancia2.config(state="disable")

"""
Funciones botones para el valor de Vmax
"""

maxVMAXvalue = 5
minVMAXvalue = -20

def incVmax():
    """
    Evento del boton ">" de vmax
    """

    enableDecVmax()
    vmaxValue = float(lblVmaxValue.cget("text"))
    vmaxValue += 0.1
    if vmaxValue >= maxVMAXvalue:
        vmaxValue = maxVMAXvalue
        disableIncVmax()
    lblVmaxValue.config(text=str(round(vmaxValue,1)))

def incVmax2():
    """
    Evento del boton ">>" de vmax
    """

    enableDecVmax()
    vmaxValue = float(lblVmaxValue.cget("text"))
    vmaxValue += 1
    if vmaxValue >= maxVMAXvalue:
        vmaxValue = maxVMAXvalue
        disableIncVmax()
    lblVmaxValue.config(text=str(round(vmaxValue,1)))

def decVmax():
    """
    Evento del boton "<" de vmax
    """
    enableIncVmax()
    vmaxValue = float(lblVmaxValue.cget("text"))
    vmaxValue -= 0.1
    if vmaxValue <= minVMAXvalue:
        vmaxValue = minVMAXvalue
        disableDecVmax()
    if vmaxValue > float(lblVminValue.cget("text")):
        lblVmaxValue.config(text=str(round(vmaxValue,1)))

def decVmax2():
    """
    Evento del boton "<<" de vmax
    """

    enableIncVmax()
    vmaxValue = float(lblVmaxValue.cget("text"))
    vmaxValue -= 1
    if vmaxValue <= minVMAXvalue:
        vmaxValue = minVMAXvalue
        disableDecVmax()
    if vmaxValue > float(lblVminValue.cget("text")):
        lblVmaxValue.config(text=str(round(vmaxValue,1)))

def enableIncVmax():
    """
    Permite habilitar los botones ">>" y ">" de vmax
    """

    btnIncVmax.config(state="normal")
    btnIncVmax2.config(state="normal")

def enableDecVmax():
    """
    Permite habilitar los botones "<<" y "<" de vmax
    """

    btnDecVmax.config(state="normal")
    btnDecVmax2.config(state="normal")

def disableIncVmax():
    """
    Permite deshabilitar los botones ">>" y ">" de vmax
    """

    btnIncVmax.config(state="disable")
    btnIncVmax2.config(state="disable")

def disableDecVmax():
    """
    Permite deshabilitar los botones "<<" y "<" de vmax
    """

    btnDecVmax.config(state="disable")
    btnDecVmax2.config(state="disable")

"""
Funciones botones para el valor de Vmin
"""

maxVMINValue = 0
minVMINValue = -40

def incVmin():
    """
    Evento del boton ">" de vmin
    """

    enableDecVmin()
    VminValue = float(lblVminValue.cget("text"))
    VminValue += 0.1
    if VminValue >= maxVMINValue:
        VminValue = maxVMINValue
        disableIncVmin()
    if VminValue < float(lblVmaxValue.cget("text")):
        lblVminValue.config(text=str(round(VminValue,1)))

def incVmin2():
    """
    Evento del boton ">>" de vmin
    """

    enableDecVmin()
    VminValue = float(lblVminValue.cget("text"))
    VminValue += 1
    if VminValue >= maxVMINValue:
        VminValue = maxVMINValue
        disableIncVmin()
    if VminValue < float(lblVmaxValue.cget("text")):
        lblVminValue.config(text=str(round(VminValue,1)))

def decVmin():
    """
    Evento del boton "<" de vmin
    """

    enableIncVmin()
    VminValue = float(lblVminValue.cget("text"))
    VminValue -= 0.1
    if VminValue <= minVMINValue:
        VminValue = minVMINValue
        disableDecVmin()
    lblVminValue.config(text=str(round(VminValue,1)))

def decVmin2():
    """
    Evento del boton "<<" de vmin
    """

    enableIncVmin()
    VminValue = float(lblVminValue.cget("text"))
    VminValue -= 1
    if VminValue <= minVMINValue:
        VminValue = minVMINValue
        disableDecVmin()
    lblVminValue.config(text=str(round(VminValue,1)))

def enableIncVmin():
    """
    Permite habilitar los botones ">>" y ">" de vmin
    """

    btnIncVmin.config(state="normal")
    btnIncVmin2.config(state="normal")

def enableDecVmin():
    """
    Permite habilitar los botones "<<" y "<" de vmin
    """

    btnDecVmin.config(state="normal")
    btnDecVmin2.config(state="normal")

def disableIncVmin():
    """
    Permite deshabilitar los botones ">>" y ">" de vmin
    """

    btnIncVmin.config(state="disable")
    btnIncVmin2.config(state="disable")

def disableDecVmin():
    """
    Permite deshabilitar los botones "<<" y "<" de vmin
    """

    btnDecVmin.config(state="disable")
    btnDecVmin2.config(state="disable")


"""
Funciones para actualizar el modo (Reconstruction o Setup) en pantalla
"""

def btnReconstruccionClicked():
    """
    Evento del boton modo Reconstruccion
    """

    global mode
    mode = "Reconstruccion"
    lblModeValue.config(text="Reconstrucción")
    btnReconstruccion.config(state='disabled')
    btnSetup.config(state='normal')

def btnSetupClicked():
    """
    Evento del boton modo Setup
    """
    global mode
    mode = "Setup"
    lblModeValue.config(text=mode)
    btnReconstruccion.config(state='normal')
    btnSetup.config(state='disabled')

"""
Funciones para obtener el vector de datos homogéneo
"""

def calibrar():
    """
    Evento del boton calibrar
    """
    ImpetomC.actualizarVectorDeDatosHomogeneo()
    btnReset.config(state="normal")

"""
Funciones botones Saved Timed
"""

maxTimeValue= 120
minTimeValue = 0.5
def incTime():
    """
    Evento del boton ">" de tiempo entre imagenes guardadas
    """

    enableDecTime()
    timeValue = float(lblSaveTimeValue.cget("text"))
    if timeValue == minTimeValue:
      timeValue = 1
    else:
        timeValue += 1
    if timeValue >= maxTimeValue:
        timeValue = maxTimeValue
        disbleIncTime()
    lblSaveTimeValue.config(text=str(round(timeValue)))

def incTime2():
    """
    Evento del boton ">>" de tiempo entre imagenes guardadas
    """

    enableDecTime()
    timeValue = float(lblSaveTimeValue.cget("text"))
    if timeValue == minTimeValue:
        timeValue = 5
    else:
        timeValue += 5
    if timeValue >= maxTimeValue:
        timeValue = maxTimeValue
        disbleIncTime()
    lblSaveTimeValue.config(text=str(round(timeValue)))

def decTime():
    """
    Evento del boton "<" de tiempo entre imagenes guardadas
    """

    enableIncTime()
    timeValue = float(lblSaveTimeValue.cget("text"))
    timeValue -= 1
    if timeValue < minTimeValue:
        timeValue = minTimeValue
        lblSaveTimeValue.config(text=str(round(timeValue,1)))
        disableDecTime()
    else:
        lblSaveTimeValue.config(text=str(round(timeValue)))

def decTime2():
    """
    Evento del boton "<<" de tiempo entre imagenes guardadas
    """

    enableIncTime()
    timeValue = float(lblSaveTimeValue.cget("text"))
    timeValue -= 5
    if timeValue < minTimeValue:
        timeValue = minTimeValue
        lblSaveTimeValue.config(text=str(round(timeValue,1)))#redondea para colocar 0.5 en pantalla
        disableDecTime()
    else:
        lblSaveTimeValue.config(text=str(round(timeValue)))

def enableIncTime():
    """
    Permite habilitar los botones ">>" y ">" del tiempo entre imagenes guardadas
    """

    btnIncTime.config(state="normal")
    btnIncTime2.config(state="normal")

def enableDecTime():
    """
    Permite habilitar los botones "<<" y "<" del tiempo entre imagenes guardadas
    """

    btnDecTime.config(state="normal")
    btnDecTime2.config(state="normal")

def disbleIncTime():
    """
    Permite deshabilitar los botones ">>" y ">" del tiempo entre imagenes guardadas
    """
    btnIncTime.config(state="disable")
    btnIncTime2.config(state="disable")

def disableDecTime():
    """
    Permite deshabilitar los botones "<<" y "<" del tiempo entre imagenes guardadas
    """

    btnDecTime.config(state="disable")
    btnDecTime2.config(state="disable")


"""
Funciones de los radioButtons de BP
"""

def enableScaleOptions():
    """
    Evento del radio button para habilitar las opciones del rango de escala
    """

    global sclRangeEnable
    sclRangeEnable = True
    enableIncVmin()
    enableIncVmax()
    enableDecVmin()
    enableDecVmax()


def disableScaleOptions():
    """
    Evento del radio button para deshabilitar las opciones del rango de escala
    """

    global sclRangeEnable
    sclRangeEnable = False
    disableDecVmin()
    disableDecVmax()
    disableIncVmax()
    disableIncVmin()

"""
Función para el control del evento de cierre de la aplicación
"""

def onClose():
    """
    Evento de cierre de la aplicacion
    """
    if tkinter.messagebox.askyesno("Salir","Está seguro que desea salir?"):
        global Reconstruir
        if Reconstruir:
            Reconstruir=False
            time.sleep(5)# esto es para que pueda finalizar el programa sin tirar error. Es por si se cierra el programa, con el programa updeteando la imagen
        root.destroy()

"""
Armado de la interfaz
Como nota general, se uso para la escala de la pantalla 1200 (width) y 750 (height), por eso se ven esos números en todo el código de aquí en más.
Esto es porque para la versión incial de la interfaz se utilizó una con ese tamaño y se logró colocar todos los widgets quedando la interfaz agradable a la vista
"""

if __name__ == '__main__':

    root = Tk()
    root.title("Impetom-C app")
    screenWidth = root.winfo_screenwidth()
    screenHeight = root.winfo_screenheight()
    rootSize = str(screenWidth) + "x" + str(screenHeight)
    root.geometry(rootSize)

    root.protocol("WM_DELETE_WINDOW", onClose)  # manejo del evento de cierre por la cruz de la interfaz
    root.resizable(width=False, height=False)  # el usuario no puede cambiar el tamaño de la ventana

    """
    Info Impetom + logo fing
    """

    # Se agrega imagen de fondo de la app
    bg = PhotoImage(file="fondo_Impetom.png")  # color azul fondo #2b70e4 amarillo #eccc6e
    # Create Canvas
    canvas1 = Canvas(root, width=screenWidth, height=screenHeight)
    canvas1.place(x=0, y=0)
    canvas1.create_image((0 / 1200) * screenWidth, 0, image=bg, anchor="nw")

    """
    Datos del paciente
    #f00 es el color rojo para cualquier SO
    """
    lblPatient = Label(root, text="Datos del paciente", bg='#2b70e4', fg='white', font=("Helvetica", 12, "bold"))
    lblPatient.place(x=(30 / 1200) * screenWidth, y=(20 / 750) * screenHeight)

    lblDate = Label(root, text="Fecha:      " + str(date.today().strftime("%b-%d-%Y")), bg='#2b70e4', fg='white',
                    font=("Helvetica", 10, "bold"))
    lblDate.place(x=(330 / 1200) * screenWidth, y=(20 / 750) * screenHeight)

    lblName = Label(root, text="Nombre:", bg='#2b70e4', fg='white', font=("Helvetica", 10, "bold"))
    lblName.place(x=(50 / 1200) * screenWidth, y=(50 / 750) * screenHeight)
    txtName = Text(root, width=30, height=1)
    txtName.place(x=(105 / 1200) * screenWidth, y=(50 / 750) * screenHeight)
    lblNameError = Label(root, fg='red', text="Este campo es obligatorio", bg='#2b70e4', font=("Helvetica", 8, "bold"))
    lblNameError.place(x=(100 / 1200) * screenWidth, y=(75 / 750) * screenHeight)

    lblCi = Label(root, text="CI:", bg='#2b70e4', fg='white', font=("Helvetica", 10, "bold"))
    lblCi.place(x=(50 / 1200) * screenWidth, y=(100 / 750) * screenHeight)
    txtCi = Text(root, width=15, height=1)
    txtCi.place(x=(100 / 1200) * screenWidth, y=(100 / 750) * screenHeight)
    lblCiError = Label(root, fg='red', text="Este campo es obligatorio", bg='#2b70e4', font=("Helvetica", 8, "bold"))
    lblCiError.place(x=(100 / 1200) * screenWidth, y=(125 / 750) * screenHeight)

    lblSexoBiologico = Label(root, text="Sexo:", bg='#2b70e4', fg='white', font=("Helvetica", 10, "bold"))
    lblSexoBiologico.place(x=(310 / 1200) * screenWidth, y=(50 / 750) * screenHeight)
    cmbBioSex = tkinter.ttk.Combobox(root, values=['Seleccionar', 'M', 'F', 'Otro'], state='readonly', width=10)
    cmbBioSex.place(x=(350 / 1200) * screenWidth, y=(50 / 750) * screenHeight)
    lblBioSexError = Label(root, text="Este campo es obligatorio", fg='red', bg='#2b70e4', font=("Helvetica", 8, "bold"))
    lblBioSexError.place(x=(315 / 1200) * screenWidth, y=(75 / 750) * screenHeight)
    cmbBioSex.current(0)

    lblBirthDate = Label(root, text="Fecha de nacimiento:", bg='#2b70e4', fg='white', font=("Helvetica", 10, "bold"))
    lblBirthDate.place(x=(215 / 1200) * screenWidth, y=(100 / 750) * screenHeight)
    txtBirthDate = Text(root, width=10, height=1)
    txtBirthDate.place(x=(350 / 1200) * screenWidth, y=(100 / 750) * screenHeight)
    lblBirthDateError = Label(root, text="Formato : dd-mm-yyyy", fg='red', bg='#2b70e4', font=("Helvetica", 8, "bold"))
    lblBirthDateError.place(x=(315 / 1200) * screenWidth, y=(125 / 750) * screenHeight)

    """
    Imagen inicial en la app
    """

    photo = ImageTk.PhotoImage(img)
    lblImg = Label(root, image=photo, width=(700 / 1200) * screenWidth, height=(500 / 750) * screenHeight)
    lblImg.place(x=(445 / 1200) * screenWidth, y=(175 / 750) * screenHeight)

    """
    botones Start, Stop, Calibrar y Reset
    """

    btnStart = Button(root, text="Start", padx=15, pady=2, command=btnStartClicked)
    btnStart.place(x=(1030 / 1200) * screenWidth, y=(690 / 750) * screenHeight)

    btnStop = Button(root, text="Stop", padx=10, pady=2, command=btnStopClicked, state='disable')
    btnStop.place(x=(1090 / 1200) * screenWidth, y=(690 / 750) * screenHeight)

    btnCalibrar = Button(root, text="Calibrar", command=calibrar)
    btnCalibrar.place(x=(400 / 1200) * screenWidth, y=(690 / 750) * screenHeight)

    btnReset = Button(root, text="Reset", command=btnResetClicked, state='disable')
    btnReset.place(x=(1100 / 1200) * screenWidth, y=(145 / 750) * screenHeight)

    """
    Control de modo de cambio de modo
    """

    lblMode = Label(root, text="Modo: ", bg='#2b70e4', font=("Helvetica", 12, "bold"))
    lblMode.place(x=(140 / 1200) * screenWidth, y=(170 / 750) * screenHeight)
    lblModeValue = Label(root, text=mode, bg='#2b70e4', font=("Helvetica", 12))
    lblModeValue.place(x=(190 / 1200) * screenWidth, y=(170 / 750) * screenHeight)

    btnReconstruccion = Button(root, text="Reconstrucción", command=btnReconstruccionClicked,
                               font=("Helvetica", 10, "bold"))
    btnReconstruccion.place(x=(110 / 1200) * screenWidth, y=(195 / 750) * screenHeight)

    btnSetup = Button(root, text="Setup", padx=10, command=btnSetupClicked, state='disable', font=("Helvetica", 10, "bold"))
    btnSetup.place(x=(210 / 1200) * screenWidth, y=(195 / 750) * screenHeight)

    lblModeMessage = Label(root, fg='#f00', bg='white')
    lblModeMessage.place(x=(80 / 1200) * screenWidth, y=(225 / 750) * screenHeight)

    """
    Opciones para la reconstrucción 
    Primero el valor p
    """
    lblImgOptions = Label(root, text="Opciones de imagen", font=("Helvetica", 10, "bold"), bg='white')
    lblImgOptions.place(x=(130 / 1200) * screenWidth, y=(245 / 750) * screenHeight)

    lblp = Label(root, text="p", bg='white')
    lblp.place(x=(177 / 1200) * screenWidth, y=(270 / 750) * screenHeight)
    initialPValue = "0.2"
    lblpRangeValue = Label(root, text=initialPValue, bg='white')  # p=0.25 para setup
    lblpRangeValue.place(x=(175 / 1200) * screenWidth, y=(295 / 750) * screenHeight)

    btnIncpRange = Button(root, text=">", command=incPRange)
    btnIncpRange.place(x=(250 / 1200) * screenWidth, y=(295 / 750) * screenHeight)

    btnIncpRange2 = Button(root, text=">>", command=incPRange2)
    btnIncpRange2.place(x=(275 / 1200) * screenWidth, y=(295 / 750) * screenHeight)

    btnDecpRange = Button(root, text="<", command=decPRange)
    btnDecpRange.place(x=(100 / 1200) * screenWidth, y=(295 / 750) * screenHeight)

    btnDecpRange2 = Button(root, text="<<", command=decPRange2)
    btnDecpRange2.place(x=(67 / 1200) * screenWidth, y=(295 / 750) * screenHeight)

    """
    Control para la frecuencia (ganancia)
    """
    lblGanancia = Label(root, text="Ganancia", bg='white')
    lblGanancia.place(x=(160 / 1200) * screenWidth, y=(330 / 750) * screenHeight)
    initialGValue = "-10"
    lblGananciaValue = Label(root, text=initialGValue, bg='white')
    lblGananciaValue.place(x=(175 / 1200) * screenWidth, y=(355 / 750) * screenHeight)

    btnIncGanancia = Button(root, text=">", command=incGanancia)
    btnIncGanancia.place(x=(250 / 1200) * screenWidth, y=(355 / 750) * screenHeight)

    btnIncGanancia2 = Button(root, text=">>", command=incGanancia2)
    btnIncGanancia2.place(x=(275 / 1200) * screenWidth, y=(355 / 750) * screenHeight)

    btnDecGanancia = Button(root, text="<", command=decGanancia)
    btnDecGanancia.place(x=(100 / 1200) * screenWidth, y=(355 / 750) * screenHeight)

    btnDecGanancia2 = Button(root, text="<<", command=decGanancia2)
    btnDecGanancia2.place(x=(67 / 1200) * screenWidth, y=(355 / 750) * screenHeight)

    """
    Control del rango de escala
    """

    lblSclRangeOptions = Label(root, text="Control de rango de escala", font=("Helvetica", 10, "bold"), bg='white')
    lblSclRangeOptions.place(x=(110 / 1200) * screenWidth, y=(400 / 750) * screenHeight)

    rbtnSclOptionsEnable = Radiobutton(root, text="On", variable=0, value=1, command=enableScaleOptions, bg='white')
    rbtnSclOptionsEnable.place(x=(280 / 1200) * screenWidth, y=(400 / 750) * screenHeight)

    rbtnSclOptionsDisable = Radiobutton(root, text="Off", variable=0, value=2, command=disableScaleOptions, bg='white')
    rbtnSclOptionsDisable.place(x=(320 / 1200) * screenWidth, y=(400 / 750) * screenHeight)
    rbtnSclOptionsDisable.select()
    """
    Control Vmax
    """

    lblVmax = Label(root, text=" Valor máximo", bg='white')
    lblVmax.place(x=(145 / 1200) * screenWidth, y=(425 / 750) * screenHeight)
    initialVmaxValue = "0.5"
    lblVmaxValue = Label(root, text=initialVmaxValue, bg='white')
    lblVmaxValue.place(x=(175 / 1200) * screenWidth, y=(450 / 750) * screenHeight)

    btnIncVmax = Button(root, text=">", command=incVmax)
    btnIncVmax.place(x=(250 / 1200) * screenWidth, y=(450 / 750) * screenHeight)

    btnIncVmax2 = Button(root, text=">>", command=incVmax2)
    btnIncVmax2.place(x=(275 / 1200) * screenWidth, y=(450 / 750) * screenHeight)

    btnDecVmax = Button(root, text="<", command=decVmax)
    btnDecVmax.place(x=(100 / 1200) * screenWidth, y=(450 / 750) * screenHeight)

    btnDecVmax2 = Button(root, text="<<", command=decVmax2)
    btnDecVmax2.place(x=(67 / 1200) * screenWidth, y=(450 / 750) * screenHeight)

    """
    Control Vmin
    """

    lblVmin = Label(root, text=" Valor mínimo", bg='white')
    lblVmin.place(x=(145 / 1200) * screenWidth, y=(480 / 750) * screenHeight)
    initialVminValue = "-5.5"
    lblVminValue = Label(root, text=initialVminValue, bg='white')
    lblVminValue.place(x=(175 / 1200) * screenWidth, y=(510 / 750) * screenHeight)

    btnIncVmin = Button(root, text=">", command=incVmin)
    btnIncVmin.place(x=(250 / 1200) * screenWidth, y=(510 / 750) * screenHeight)

    btnIncVmin2 = Button(root, text=">>", command=incVmin2)
    btnIncVmin2.place(x=(275 / 1200) * screenWidth, y=(510 / 750) * screenHeight)

    btnDecVmin = Button(root, text="<", command=decVmin)
    btnDecVmin.place(x=(100 / 1200) * screenWidth, y=(510 / 750) * screenHeight)

    btnDecVmin2 = Button(root, text="<<", command=decVmin2)
    btnDecVmin2.place(x=(67 / 1200) * screenWidth, y=(510 / 750) * screenHeight)

    """
    Control del Modelo
    """

    lblContorno = Label(root, text="Especificaciones de contorno", font=("Helvetica", 10, "bold"), bg='white')
    lblContorno.place(x=(110 / 1200) * screenWidth, y=(550 / 750) * screenHeight)

    lblModel = Label(root, text="Forma", bg='white')
    lblModel.place(x=(160 / 1200) * screenWidth, y=(575 / 750) * screenHeight)

    cmbModel = tkinter.ttk.Combobox(root, values=['Thorax', 'Circle'], state='readonly')
    cmbModel.place(x=(120 / 1200) * screenWidth, y=(600 / 750) * screenHeight)
    cmbModel.current(0)  # el valor inicial de comboBox es Thorax

    """
    Control de Medida
    """

    lblMedida = Label(root, text="Perímetro (mm)", bg='white')
    lblMedida.place(x=(260 / 1200) * screenWidth, y=(575 / 750) * screenHeight)

    txtMedida = Text(root, width=4, height=1)
    txtMedida.place(x=(280 / 1200) * screenWidth, y=(600 / 750) * screenHeight)

    lblMedidaError = Label(root, text="Campo obligatorio", fg='#f00', bg='white')
    lblMedidaError.place(x=(265 / 1200) * screenWidth, y=(620 / 750) * screenHeight)

    """
    Control de Medida_Z
    """

    lblMedidaZ = Label(root, text="Altura (mm)", bg='white')
    lblMedidaZ.place(x=(350 / 1200) * screenWidth, y=(575 / 750) * screenHeight)

    txtMedidaZ = Text(root, width=4, height=1)
    txtMedidaZ.place(x=(360 / 1200) * screenWidth, y=(600 / 750) * screenHeight)

    lblMedidaZError = Label(root, text="Campo obligatorio", fg='#f00', bg='white')
    lblMedidaZError.place(x=(345 / 1200) * screenWidth, y=(550 / 750) * screenHeight)

    """
    Control de tiempos para guardado de imágenes para historia clínica
    """

    lblSaveTime = Label(root, text="Tiempo de guardado (min)", font=("Helvetica", 10, "bold"), bg='white')
    lblSaveTime.place(x=(100 / 1200) * screenWidth, y=(635 / 750) * screenHeight)
    initialSaveTimeValue = "5"
    lblSaveTimeValue = Label(root, text=initialSaveTimeValue, bg='white')
    lblSaveTimeValue.place(x=(175 / 1200) * screenWidth, y=(665 / 750) * screenHeight)

    btnIncTime = Button(root, text=">", command=incTime)
    btnIncTime.place(x=(250 / 1200) * screenWidth, y=(665 / 750) * screenHeight)

    btnIncTime2 = Button(root, text=">>", command=incTime2)
    btnIncTime2.place(x=(275 / 1200) * screenWidth, y=(665 / 750) * screenHeight)

    btnDecTime = Button(root, text="<", command=decTime)
    btnDecTime.place(x=(100 / 1200) * screenWidth, y=(665 / 750) * screenHeight)

    btnDecTime2 = Button(root, text="<<", command=decTime2)
    btnDecTime2.place(x=(67 / 1200) * screenWidth, y=(665 / 750) * screenHeight)

    lblTimeError = Label(root, text="", fg='#f00', bg='white')
    lblTimeError.place(x=(100 / 1200) * screenWidth, y=(700 / 750) * screenHeight)

    """
    Por problemas con Arduino al inicio de la aplicación
    #2b70e4es el color azul de la imagen del banner
    """

    lblError = Label(root, text="", bg='#2b70e4', font=("Helvetica", 10, "bold"), fg="red")
    lblError.place(x=(440 / 1200) * screenWidth, y=(150 / 750) * screenHeight)
    disableScaleOptions()  # Comienzo el programa con la escala deshabilitada
    try:
        ImpetomC.startUp()
        arduinoImpetom = 1  # Indicador que arduinoImpetom esta conectado
    except ImpetomCError as e:
        lblError.config(text=str(e) + " Revise la conexión al dispositivo y presione Start", bg='white')
        arduinoImpetom = 0

    root.mainloop()