Documentos de Académico
Documentos de Profesional
Documentos de Cultura
FACULTAD DE INGENIERÍA
Proyecto: BookLab
Integrantes:
Sección: IX56
RESUMEN
Curso de software para ingenieros para resolver un problema en una situación del mundo real.
En este caso, la intención es permitir que la biblioteca comunitaria ingrese el código y el usuario
Biblioteca de Surquillo que intenta optimizar estos procesos. Esto le permite identificar a los
usuarios que regresan y los libros más solicitados, incluidas las fechas más solicitadas. Por lo
que se puede segmentar según estos tres criterios, facilitando las solicitudes según las
Primero, a través del diagrama de flujo, proponemos un orden lógico para la estructura
préstamo hasta la devolución del libro. Por otra parte, se obtendrá una base de datos actualizada
que contiene el nombre del libro (que establece el código en función del nombre) y el código
de cada usuario. Por ello, entendemos que se debe establecer una matriz óptima para lograr
estos objetivos.
Luego configure la GUI para obtener una imagen clara y verifique la validez de la
con el historial de cada usuario, lo que hace que las solicitudes de libros sean más eficientes.
2
ÍNDICE
RESUMEN ............................................................................................................................ 2
CAPÍTULO 1 ........................................................................................................................ 6
PRESENTACIÓN ............................................................................................................................. 6
CAPÍTULO 2 ........................................................................................................................ 7
MARCO TEÓRICO ......................................................................................................................... 7
CAPÍTULO 3 ........................................................................................................................ 8
DESCRIPCIÓN DE LA EMPRESA ............................................................................................... 8
MISIÓN ............................................................................................................................................. 8
VISIÓN .............................................................................................................................................. 8
VALORES ......................................................................................................................................... 9
PROCESO DE ESTUDIO Y DEFINICIÓN DEL PROBLEMA ................................................. 9
OBJETIVO GENERAL ................................................................................................................. 10
OBJETIVOS ESPECÍFICOS ........................................................................................................ 10
HIPÓTESIS DE SOLUCIÓN ........................................................................................................ 11
CAPÍTULO 4 ...................................................................................................................... 11
PROPUESTA DE MEJORA CUALITATIVA ............................................................................ 11
PROTOTIPO DE INTERFÁZ ...................................................................................................... 15
TABLA DE VARIABLES .............................................................................................................. 23
CAPÍTULO 5 ...................................................................................................................... 27
PROPUESTA DE ALGORITMO Y DE LOS COMPONENTES PYTHON ........................... 27
HERRAMIENTAS, MODULOS, UTILIZADOS PARA EL DESARROLLO......................... 28
CONCLUSIONES Y RECOMENDACIONES ............................................................................ 29
GLOSARIO ........................................................................................................................ 29
BIBLIOGRAFÍA ................................................................................................................. 30
ANEXOS ............................................................................................................................. 31
3
INDICE DE FIGURAS
Figura 17: Interfaz desde la vista del administrador para agregar un libro 22
4
INDICE DE TABLAS
5
CAPÍTULO 1
PRESENTACIÓN
como función principal brindar el soporte técnico necesario para los dispositivos y equipos.
registro y seguimiento. Actualmente, los nuevos clientes que deseen registrar un nuevo libro
son ingresados manualmente en el libro de control por parte del gerente del área, ordenados y
Por ello, se puso en plan realizar un análisis creando un diagrama de Pareto asociado a
las métricas del último periodo en el que se identificó el principal problema en el área de
repuesto y los informes del personal técnico. En pocas palabras, este proyecto brindará
el fin de evitar retrasos en la producción que se pretenda generar, cuyo objetivo es lanzar al
mercado.
6
CAPÍTULO 2
MARCO TEÓRICO
pero las organizaciones crecen a un ritmo de cambio lineal. El impacto de estas tecnologías es
inmenso y hoy en día se utilizan ampliamente y se consideran una ayuda para personas de todo
el mundo. Por otro lado, nuestro proyecto se basa en el proceso de administración y gestión de
bibliotecas de BookLab, por lo que uno de los principales objetivos es poder ayudar con los
tiempo que dedica a estas tareas. Además, la digitalización de diversos procesos nos permite
asegurar y mejorar su gestión. Esta es una mejora al pasar varias auditorías de sistemas de
gestión. Domina la forma en que vivimos y trabajamos en una era como la Cuarta Revolución
país al mismo tiempo que garantizamos la seguridad que necesitan nuestros clientes.
7
CAPÍTULO 3
DESCRIPCIÓN DE LA EMPRESA
ciudadano. Cuenta con alrededor de 100 libros de diferentes categorías para el disfrute de
nuestros usuarios.
Nota: BookLab
MISIÓN
Ofrecer una amplia gama de libros de texto, lecturas e innovaciones digitales para
satisfacer plenamente las necesidades de nuestros clientes. Con múltiples editoriales, satisfacer
VISIÓN
comunidad educativa.
8
VALORES
✓ Responsabilidad
✓ Respeto
✓ Honestidad
✓ Innovación
✓ Organización
El presente trabajo intenta darle solución a la problemática que aqueja con mayor
una encuesta virtual voluntaria a una muestra de 1000 vecinos. Obteniendo como resultado que
el principal inconveniente que ellos reportan es la carencia de una plataforma virtual para
realizar la reserva de algún libro, con el fin de evitar largas colas y papeleo innecesario que
realización de una base de datos con los libros que cuenta la institución para facilitar la reserva
de los títulos y además llevar un reporte inmediato de cada solicitud efectuada. Además,
implementar una interfaz en la plataforma Python con dos botones principales que permitirán
desarrolladores).
9
Tabla 1: Encuesta de incidencias realizada a 1000 vecinos
N INDICE N. DE
RESPUESTAS
1 Falta de una plataforma virtual para la 510
reserva de libros
2 Largas colas en la sede de la biblioteca 260
municipal
3 Pocos títulos en el catálogo de la 120
biblioteca
4 Otros 110
Total 1000
Otros
OBJETIVO GENERAL
OBJETIVOS ESPECÍFICOS
10
➢ Mostrar usuarios frecuentes
➢ Obtenga información sobre títulos y géneros populares
➢ Identificar el promedio de días para reservar un libro, hasta de 1 a 8 días, según
política municipal.
➢ Promoción de la Lectura Familiar en el Distrito de Surquillo
➢ Eliminar las largas colas en el recinto principal
➢ Crear una lista de espera e informe a las personas cuántos días estará disponible el
libro
➢ Generar nuevos ingresos a través de su programa
HIPÓTESIS DE SOLUCIÓN
Ante el gran avance de la tecnología es indispensable contar con una plataforma virtual
que permita a los usuarios contar con los medios técnicos para que su día a día no cambie
abruptamente. Con esto en mente, recomendamos crear una app que permita a los usuarios
reservar de manera ordenada y muy fácil, evitando traslados a la sede y colas obvias en el sitio.
Como resultado, todos tienen acceso instantáneo a información precisa sobre qué libros están
disponibles y cuáles no. Todo esto garantiza una gestión precisa de los libros y genera
CAPÍTULO 4
concluyó que su principal queja estaba relacionada con la falta de una plataforma virtual para
solicitar reservas. Actualmente, es necesario acudir a la sede, y puede haber casos en los que el
título deseado no esté disponible. Por lo tanto, para optimizar y darle solución a este problema
se propone crear una interfaz utilizando el lenguaje de programación Python conectada a una
base de datos que brinde información relevante y oportuna a los usuarios que deseen reservar
títulos del catálogo. Este programa permite averiguar fácilmente si hay copias disponibles y si
11
las copias están reservadas simplemente ingresando datos como la cantidad de días reservados,
la fecha de salida y la fecha de regreso. Se crearon varios diagramas para ser presentados con
12
Figura 4: Diagrama de clases
13
Figura 6: Diagrama de secuencia para el usuario
14
Figura 7: Diagrama de actividades
PROTOTIPO DE INTERFÁZ
Para poder visualizar nuestra propuesta de mejora se creó una ventana a manera de
En esta ventana se presentará la pantalla en la que se encontrarán los casilleros para que
pueda ingresar los datos correspondientes como el DNI y la contraseña. Tal como se muestra
en la figura 8.
15
Figura 8: Interfaz inicial de la app BookLab
En la figura 9 se muestra la interfaz del usuario para que este se pueda registrar, de
manera que pueda ingresar todos sus datos como su nombre, DNI y contraseña que servirá para
16
Figura 9: Interfaz para registrarse siendo usuario
Luego de validar que todos los datos ingresados hayan sido correctos, se le redirecciona
a otra ventana donde se le mostrará las opciones que tiene como usuario.
17
Figura 10: Interfaz donde se muestra las opciones del usuario
en la figura 11
18
Si le damos a la pestaña que dice ver, nos mostrará automáticamente todos los libros de
todas las categorías disponibles, si le picamos en alguna categoría en específica nos mostrará
Una vez seleccionado el título del libro solicitado el usuario deberá indicar la fecha en
el que se va a retirar el libro y los días de reserva. Cabe resaltar que los días de reserva, es decir
los días que el usuario pueda quedarse con los libros, es máximo 7 días por norma
administrativa.
19
Figura 13: Interfaz para reservar el libro seleccionado
libro.
Por otro lado, teniendo en cuenta la perspectiva del encargado o administrador, este
Una vez comprobado que los datos ingresados son correctos, el sistema procede a
20
Figura 15: Interfaz del administrador donde da dos opciones
lanzaremos un reporte de manera gráfica de las categorías reservadas de los libros más
21
Finalmente, si en caso de que se quiera agregar un libro por el administrador tendrá que
del mismo.
Figura 17: Interfaz desde la vista del administrador para agregar un libro
22
TABLA DE VARIABLES
23
Tabla 3: Variables del programa realizado
24
Tabla 4: Variables del programa realizado
25
Tabla 5: Variables del programa realizado
26
CAPÍTULO 5
Para el desarrollo del programa se utilizó el lenguaje de programación Python con las
respectivas librerías tales como matplotlib, numpy, openpyxl, pandas, datetime, collections,
etc. El principal es el tkinter, que con sus métodos nos permite crear formularios e interfaces
gráficas que hacen más amigable y entendible el programa. Es por este medio que ingresaremos
los diversos datos previamente explicados donde se almacenarán apoyado con una base de
datos que, en este caso es Excel, Con esto, administraremos la data y de igual manera para el
27
Figura 18: Parte del código en Python
28
CONCLUSIONES Y RECOMENDACIONES
sistema automatizado que pueda agilizar diversos procesos, de manera que este satisfaga la
propuesto ya que este ayudaría a la gestión y optimización de la misma. Asimismo, seguir con
la labor que están haciendo de alguna manera impulsar a la lectura tanto para niños como para
adultos.
GLOSARIO
29
BIBLIOGRAFÍA
programming, data science, and web development with Python 3.7, 2nd Edition (2a
Carlos, J., Castellanos, P., Dougnac, J., Merino Echeverría, D., & Bermeo Ramírez, J. (2020).
https://do1.dr-chuck.com/pythonlearn/ES_es/pythonlearn.pdf
https://bibdigital.epn.edu.ec/bitstream/15000/21099/1/CD%2010612.pdf
https://www.math.ru.nl/~bosma/onderwijs/najaar09/thinkpython.pdf
30
ANEXOS
CODIGO COMPLETO
from tkinter import *
import os
from matplotlib import image
import numpy as np
import matplotlib.pyplot as plt
from openpyxl import load_workbook
from tkinter import ttk
import tkinter.font as tkFont
import pandas as pd
import datetime
from PIL import ImageTk, Image
from tkinter import filedialog
from collections import namedtuple
ruta = os.getcwd()
datafile = ruta + "/Data/biblioteca.xlsx"
resourcesfolder = "Recursos/"
bookfolders = "ImagenesLibros"
31
#NOTIFICACIÓN VENTANA
def mostrarNotificacion(titulo, mensaje):
def fun_cerrar():
notificacion.destroy()
notificacion = Tk()
notificacion.geometry("330x150")
notificacion.title(titulo)
notificacion.resizable(False,False)# NO REDIMENCIONABLE
notificacion.config(background = "#94D0CC")
notificacion.mainloop()
notificacion = Tk()
notificacion.geometry("300x160")
notificacion.title(titulo)
notificacion.resizable(False,False)
notificacion.config(background = "#94D0CC")
32
button_aceptar = Button(notificacion, text = "ACEPTAR", width=25, bg = "#EDFFEC", command
= fun_cerrar)
button_aceptar.place( x = 40, y = 90)
notificacion.mainloop()
return app
def registrarUsuario():
global usuarios
nombre_error.set("")
dni_error.set("")
password_error.set("")
if entry_NOMBRE.get() == "":
nombre_error.set("El campo nombre no puede estar vacío")
return
if len(entry_NOMBRE.get()) > 15:
nombre_error.set("El campo nombre no puede tener más de 15 caracteres")
return
33
if entry_DNI.get() == "":
dni_error.set("El campo DNI no puede estar vacío")
return
try:
var = int(entry_DNI.get())
except:
dni_error.set("El DNI solo debe contener números")
return
if len(entry_DNI.get()) != 8:
dni_error.set("El campo DNI consta de 8 digitos")
return
if entry_PASSWORD.get() == "":
password_error.set("El campo contraseña no puede estar vacío")
return
if entry_PASSWORD.get() != entry_PASSWORD_2.get():
password_error.set("Las contraseñas no coinciden")
return
col_DNI = 1
col_NOMBRE = 2
col_PASSWORD = 3
col_ROL = 4
34
celdaDNI = hoja_usuarios.cell(row = len(usuarios.index) , column = col_DNI)
celdaDNI.value = entry_DNI.get()
archivo.save(datafile)
frame_createUser.pack_forget()
frame_login.pack()
mostrarNotificacion("Creación de cuenta correcta","Se ha creado correctamente su cuenta")
ancho_entry = 30
pos_x = 80
pos_y = 30
aumento_y = 25
ventanaRegistro = Frame(ventanaContenedor)
ventanaRegistro.config(width = TAM_BIBLIOTECA_APP_Y, height=
TAM_BIBLIOTECA_APP_X, bg = COLOR_FONDO_PANTALLA_1)
nombre_error = StringVar()
label_NOMBRE_Error = Label(ventanaRegistro, textvariable = nombre_error, bg =
COLOR_FONDO_PANTALLA_1, fg = "red")
35
label_NOMBRE_Error.place( x = pos_x, y = pos_y + aumento_y *2)
dni_error = StringVar()
label_DNI_Error = Label(ventanaRegistro, textvariable = dni_error, bg =
COLOR_FONDO_PANTALLA_1, fg = "red")
label_DNI_Error.place( x = pos_x, y = pos_y + aumento_y *6)
password_error = StringVar()
label_PASSWORD_Error = Label(ventanaRegistro, textvariable = password_error, bg =
COLOR_FONDO_PANTALLA_1, fg = "red" )
label_PASSWORD_Error.place(x = pos_x, y = pos_y + aumento_y * 10)
36
button_ingresar = Button(ventanaRegistro,font = FUENTE, text = "INGRESAR", width = 10, bg =
COLOR_BUTTON_1, command = registrarUsuario)
button_ingresar.place(x = pos_x + 140, y = pos_y + aumento_y * 16)
return ventanaRegistro
def regUser(event):
frame_login.pack_forget()
frame_createUser.pack()
def validarIngreso():
global usuario_autorizado
valido = True
if (DNI.get() == ""):
dni_error.set("El campo DNI no puede estar vacío.")
valido = False
if (PASSWORD.get() == ""):
password_error.set("El campo contraseña no puede estar vacío")
valido = False
if (len(usuario_autorizado) == 0):
dni_error.set("No se encuentra al usuario")
password_error.set("")
valido = False
elif (len(usuario_autorizado) == 2):
dni_error.set("Existen DNIs duplicados")
37
password_error.set("")
valido = False
return valido
def fun_ingresar_login():
#if (True):#validarIngreso()):
if (validarIngreso()):
ventanaContenedor.geometry(TAM_BIBLIOTECA_APP_2)
frame.destroy()
if (usuarios["rol"].iloc[usuario_autorizado[0]] == "user"):
frame_bienvenida.pack()
elif (usuarios["rol"].iloc[usuario_autorizado[0]] == "admin"):
frame_bienvenida_adm.pack()
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_Y, height= TAM_BIBLIOTECA_APP_X, bg =
"white")
DNI = StringVar()
entry_DNI = Entry(frame, textvariable = DNI, bg = COLOR_ENTRY_1 , width = ancho_entry)
entry_DNI.place(x = pos_x, y = pos_y + aumento_y * 1)
dni_error = StringVar()
label_DNI_Error = Label(frame, textvariable = dni_error, bg = "white", fg = "red")
label_DNI_Error.place( x = pos_x, y = pos_y + aumento_y *2)
38
PASSWORD = StringVar()
entry_PASSWORD = Entry(frame, textvariable = PASSWORD, bg = COLOR_ENTRY_1, width
= ancho_entry,show="*")
entry_PASSWORD.place( x = pos_x , y = pos_y + aumento_y * 5)
password_error = StringVar()
label_PASSWORD_Error = Label(frame, textvariable = password_error, bg = "white", fg = "red" )
label_PASSWORD_Error.place(x = pos_x, y = pos_y + aumento_y * 6)
label_crearUsuario = Label(frame, text = "Haga click aquí para crear cuenta", bg = "white", fg =
"blue", font = FUENTE)
label_crearUsuario.place(x = pos_x - 22, y = pos_y + aumento_y * 10 - 10)
label_crearUsuario.bind("<Button-1>", regUser)
return frame
def fun_ir_a_busqueda():
frame.pack_forget()
frame_busqueda = crearFrameBusquedaReserva(ventanaContenedor)
frame_busqueda.pack()
def fun_ir_a_devolver():
frame.pack_forget()
frame_retornar = crearFrameDevolver(ventanaContenedor)
frame_retornar.pack()
39
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_1)
return frame
## CREACIÓN DE FRAME
def crearFrameReservar(ventanaContenedor):
def fun_ir_a_frameReservar():
frame.pack_forget()
frame_bienvenida.pack()
def fun_ir_a_verTodo():
global frame_global
frame.pack_forget()
frame_global = crearFrameReservarContenido(ventanaContenedor,"TODOS")
40
frame_global.pack()
def fun_ir_a_verCuentos():
global frame_global
frame.pack_forget()
frame_global = crearFrameReservarContenido(ventanaContenedor,"CUENTO")
frame_global.pack()
def fun_ir_a_verNovelas():
global frame_global
frame.pack_forget()
frame_global = crearFrameReservarContenido(ventanaContenedor,"NOVELA")
frame_global.pack()
def fun_ir_a_verCienciaFiccion():
global frame_global
frame.pack_forget()
frame_global = crearFrameReservarContenido(ventanaContenedor,"CIENCIA FICCION")
frame_global.pack()
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_1)
41
button_novelas = Button(frame, image = click_novelas, borderwidth = 0,command =
fun_ir_a_verNovelas)
button_novelas.place(x = 60, y = 200)
button_verTodo = Button(frame,font = FUENTE, text = "Ver todas las categorías", width = 40, bg
= COLOR_BUTTON_1, command = fun_ir_a_verTodo)
button_verTodo.place( x = 180, y = 450)
return frame
def fun_ir_a_solicitarPrestamo():
seleccion = tablaContenido.focus()
valores = tablaContenido.item(seleccion,"values")
if(valores[4] == 0):
errorlabel.set("El libro seleccionado no se encuentra disponible. Stock: 0")
else:
errorlabel.set("")
crearVentanaSolicitarPrestamo(valores[1],valores[2])
def fun_borrar():
for item in tablaContenido.get_children():
42
tablaContenido.delete(item)
def fun_llenarTabla():
fun_borrar()
if (categoria == "TODOS"):
for i in libros.index:
if i == 0:
continue
if (texto.get() == "") or (texto.get().capitalize() in str(libros["nombre"][i]).capitalize()):
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(i),libros["codigo"][i],libros["nomb
re"][i], libros["tipo"][i], libros["disponibilidad"][i]))
else:
indice = 0
for i in libros.index:
if i == 0:
continue
if(libros["tipo"][i] == categoria) and (texto.get().capitalize() in
str(libros["nombre"][i]).capitalize()):
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),libros["codigo"][i],libros["nombre"][i], libros["tipo"][i], libros["disponibilidad"][i]))
indice += 1
s = ttk.Style()
s.theme_use('clam')
s.configure('Treeview.Heading', background=COLOR_BUTTON_1)
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_1)
43
label_categoria = Label(frame, text = categoria, bg = COLOR_FONDO_PANTALLA_1, font =
(FUENTE_LETRA, FUENTE_TAM + 10, FUENTE_WEIGHT))
label_categoria.place( x = 200, y = 30)
frame_2 = Frame(frame)
frame_2.config(width = TAM_BIBLIOTECA_APP_X_2, height=
TAM_BIBLIOTECA_APP_Y_2, bg = COLOR_FONDO_PANTALLA_1)
frame_2.place ( x = 50, y = 70)
errorlabel = StringVar()
label_error = Label(frame, textvariable = errorlabel, bg = COLOR_FONDO_PANTALLA_1,fg =
"red", font = (FUENTE_LETRA, FUENTE_TAM, FUENTE_WEIGHT))
label_error.place( x = 50, y = 500)
scrollTabla = Scrollbar(frame_2)
scrollTabla.pack(side=RIGHT, fill=Y)
tablaContenido.heading("#0",text="",anchor=CENTER)
tablaContenido.heading("id",text="Id",anchor=CENTER)
tablaContenido.heading("Codigo",text="Codigo",anchor=CENTER)
tablaContenido.heading("Titulo",text="Titulo",anchor=CENTER)
tablaContenido.heading("genero",text="Categoria",anchor=CENTER)
tablaContenido.heading("Disponibilidad",text="Dispon.",anchor=CENTER)
44
texto = StringVar()
entry_busqueda = Entry(frame, textvariable = texto,width = 12, font = (FUENTE_LETRA,
FUENTE_TAM + 2, FUENTE_WEIGHT))
entry_busqueda.place(x = 450 , y = 33)
fun_llenarTabla()
tablaContenido.pack()
return frame
fecha_aux = datetime.datetime.strptime(fecha,"%d-%m-%Y")
fechafin = fecha_aux + datetime.timedelta(days = dias)
filaPrestamos = len(prestamos.index)
celdaDNI = hoja_prestamos.cell(row = filaPrestamos, column = 1)
celdaDNI.value = str(user)
45
celdaCodigo = hoja_prestamos.cell(row = filaPrestamos, column = 2)
celdaCodigo.value = codigo
celdaFecha = hoja_prestamos.cell(row = filaPrestamos, column = 3)
celdaFecha.value = fecha
celdaDias = hoja_prestamos.cell(row = filaPrestamos, column = 4)
celdaDias.value = dias
celdaDevuelto = hoja_prestamos.cell(row = filaPrestamos, column = 5)
celdaDevuelto.value = 0
archivo.save(datafile)
ventana.destroy()
frame_global.destroy()
frame_reservar.pack()
ventana = Toplevel()
ventana.geometry(TAM_BIBLIOTECA_APP_2)
46
ventana.title("Solicitud de prestamo")
ventana.resizable(False,False)
ventana.config(background = COLOR_FONDO_PANTALLA_1)
frame = Frame(ventana)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_1)
img=Image.open(bookfolders+"/"+codigo)
o_size=img.size
f_size=(250,350)
factor = min(float(f_size[1])/o_size[1], float(f_size[0])/o_size[0])
width = int(o_size[0] * factor)
height = int(o_size[1] * factor)
rImg= img.resize((width, height), Image.ANTIALIAS)
img2 = ImageTk.PhotoImage(rImg)
panel = Label(frame, image=img2)
panel.place(x= 40, y = 150)
pos_x_label = 350
pos_y_label = 150
aumento_y_label = 60
pos_x_label_text = 550
47
usuario = usuarios["NOMBRE COMPLETO"].iloc[usuario_autorizado[0]]
label_usuario_texto = Label(frame, text = usuario, bg = COLOR_FONDO_PANTALLA_1, font =
(FUENTE_LETRA, FUENTE_TAM + 6, FUENTE_WEIGHT))
label_usuario_texto.place(x = pos_x_label_text , y = pos_y_label + aumento_y_label * 0)
frame.pack()
48
ventana.mainloop()
def borrarTabla():
for item in tablaContenido.get_children():
tablaContenido.delete(item)
49
else:
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["codigo"][i],libros["nombre"][j],
prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Por devolver"))
indice += 1
break
elif comboTipo.get() == comboTipoValores[1]:
for i in prestamos.index:
if i == 0:
continue
if prestamos["DNI"][i] == usuarios["DNI"].iloc[usuario_autorizado[0]]:
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if(prestamos["devuelto"][i] == 0):
if fechafin < datetime.datetime.today():
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i], fechafin.strftime("%d-
%m-%Y"),"Atrasado"))
indice += 1
break
elif comboTipo.get() == comboTipoValores[2]:
for i in prestamos.index:
if i == 0:
continue
if prestamos["DNI"][i] == usuarios["DNI"].iloc[usuario_autorizado[0]]:
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if (prestamos["devuelto"][i] == 1):
50
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i], fechafin.strftime("%d-
%m-%Y"),"Devuelto"))
indice += 1
break
elif comboTipo.get() == comboTipoValores[3]:
for i in prestamos.index:
if i == 0:
continue
if prestamos["DNI"][i] == usuarios["DNI"].iloc[usuario_autorizado[0]]:
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if(prestamos["devuelto"][i] == 0):
if fechafin >= datetime.datetime.today():
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i], fechafin.strftime("%d-
%m-%Y"),"Por devolver"))
indice += 1
break
s = ttk.Style()
s.theme_use('clam')
s.configure('Treeview.Heading', background=COLOR_BUTTON_1)
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_1)
51
button_retroceder.place( x = 20, y = 30)
frame_2 = Frame(frame)
frame_2.config(width = TAM_BIBLIOTECA_APP_X_2, height=
TAM_BIBLIOTECA_APP_Y_2, bg = COLOR_FONDO_PANTALLA_1)
frame_2.place ( x = 20, y = 100)
scrollTabla = Scrollbar(frame_2)
scrollTabla.pack(side=RIGHT, fill=Y)
52
tablaContenido.heading("#0",text="",anchor=CENTER)
tablaContenido.heading("id",text="Id",anchor=CENTER)
tablaContenido.heading("Codigo",text="Codigo",anchor=CENTER)
tablaContenido.heading("Titulo",text="Titulo",anchor=CENTER)
tablaContenido.heading("fecha",text="Fecha Prestamo",anchor=CENTER)
tablaContenido.heading("fechafin",text="Fecha Fin",anchor=CENTER)
tablaContenido.heading("estado",text="Estado",anchor=CENTER)
actualizarTabla()
tablaContenido.pack()
return frame
archivo.save(datafile)
53
prestamos = generarPandas(hoja_prestamos)
prestamos[["DNI"]] = prestamos[["DNI"]].astype(str)
fun_ir_a_retroceder()
mostrarNotificacion("Devolución correcta","Se ha hecho una correcta devolución")
def fun_ir_a_retroceder():
frame.destroy()
frame_bienvenida.pack()
s = ttk.Style()
s.theme_use('clam')
s.configure('Treeview.Heading', background=COLOR_BUTTON_1)
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_1)
frame_2 = Frame(frame)
frame_2.config(width = TAM_BIBLIOTECA_APP_X_2, height=
TAM_BIBLIOTECA_APP_Y_2, bg = COLOR_FONDO_PANTALLA_1)
frame_2.place ( x = 20, y = 70)
scrollTabla = Scrollbar(frame_2)
scrollTabla.pack(side=RIGHT, fill=Y)
54
tablaContenido = ttk.Treeview(frame_2,yscrollcommand=scrollTabla.set, height = 20,
selectmode='browse')
tablaContenido.pack()
scrollTabla.config(command=tablaContenido.yview)
tablaContenido.heading("#0",text="",anchor=CENTER)
tablaContenido.heading("id",text="Id",anchor=CENTER)
tablaContenido.heading("Codigo",text="Codigo",anchor=CENTER)
tablaContenido.heading("Titulo",text="Titulo",anchor=CENTER)
tablaContenido.heading("fecha",text="Fecha Prestamo",anchor=CENTER)
tablaContenido.heading("fechafin",text="Fecha Fin",anchor=CENTER)
tablaContenido.heading("estado",text="Estado",anchor=CENTER)
indice = 0
for i in prestamos.index:
if prestamos["DNI"][i] == usuarios["DNI"].iloc[usuario_autorizado[0]]:
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if(prestamos["devuelto"][i] == 0):
if fechafin < datetime.datetime.today():
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i], fechafin.strftime("%d-
%m-%Y"),"Atrasado"))
55
else:
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i], fechafin.strftime("%d-
%m-%Y"),"Por devolver"))
indice += 1
break
tablaContenido.pack()
return frame
####################################################################
def fun_ir_a_addlibros():
frame.pack_forget()
frame_addlibro.pack()
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_2)
56
label_bienvenida = Label(frame, text = "BIENVENID@ a BookLab", bg =
COLOR_FONDO_PANTALLA_2, font = (FUENTE_LETRA, FUENTE_TAM + 10,
FUENTE_WEIGHT))
label_bienvenida.place( x = 200, y = 70)
return frame
def borrarTabla():
for item in tablaContenido.get_children():
tablaContenido.delete(item)
57
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if (prestamos["devuelto"][i] == 1):
tablaContenido.insert(parent='',index='end',iid=i,text='',
values=(str(indice + 1),prestamos["DNI"][i],prestamos["codigo"][i]
,libros["nombre"][j], prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Devuelto"))
elif(prestamos["devuelto"][i] == 0):
if fechafin < datetime.datetime.today():
tablaContenido.insert(parent='',index='end',iid=i,text='',
values=(str(indice + 1),prestamos["DNI"][i],prestamos["codigo"][i]
,libros["nombre"][j], prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Atrasado"))
else:
tablaContenido.insert(parent='',index='end',iid=i,text='',
values=(str(indice + 1),prestamos["DNI"][i],prestamos["codigo"][i]
,libros["nombre"][j], prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Por devolver"))
indice += 1
break
elif comboTipo.get() == comboTipoValores[1]:
for i in prestamos.index:
if i == 0:
continue
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if(prestamos["devuelto"][i] == 0):
if fechafin < datetime.datetime.today():
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["DNI"][i],prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Atrasado"))
indice += 1
58
break
elif comboTipo.get() == comboTipoValores[2]:
for i in prestamos.index:
if i == 0:
continue
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if (prestamos["devuelto"][i] == 1):
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["DNI"][i],prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Devuelto"))
indice += 1
break
elif comboTipo.get() == comboTipoValores[3]:
for i in prestamos.index:
if i == 0:
continue
for j in libros.index:
if libros["codigo"][j] == prestamos["codigo"][i]:
fecha = datetime.datetime.strptime(prestamos["fechaRegistro"][i],"%d-%m-%Y")
dias = int(prestamos["dias"][i])
fechafin = fecha + datetime.timedelta(days = dias)
if(prestamos["devuelto"][i] == 0):
if fechafin >= datetime.datetime.today():
tablaContenido.insert(parent='',index='end',iid=i,text='',values=(str(indice +
1),prestamos["DNI"][i],prestamos["codigo"][i],libros["nombre"][j], prestamos["fechaRegistro"][i],
fechafin.strftime("%d-%m-%Y"),"Por devolver"))
indice += 1
break
def mostrarPorcentajeCategoria():
prestamoCategoria = []
vecesPrestados = []
59
for genero in generosLibros:
var = libros.loc[libros["tipo"] == genero, "vecesPrestado"].sum()
if ( var > 0):
prestamoCategoria.append(genero)
vecesPrestados.append(var)
def mostrarLibrosMasSolicitados():
nombre_Libros = []
cant_Prestamos = []
for i in libros.index:
if (i != 0) and (libros["vecesPrestado"][i] != 0):
print(i)
nombre_Libros.append(libros["nombre"][i])
cant_Prestamos.append(libros["vecesPrestado"][i])
plt.barh(nombre_Libros, cant_Prestamos)
plt.title("Número de prestamos por libro")
plt.ylabel("Nombres")
plt.xlabel("Número de prestamos")
plt.show()
def mostrarUsuarioConMasPrestamos():
usuarioPrestamo = []
cantPrestamos = []
for i in usuarios.index:
if (i == 0) or (usuarios["rol"][i] == "admin"):
continue
var = prestamos.loc[prestamos["DNI"] == usuarios["DNI"][i],"DNI"].count()
60
usuarioPrestamo.append(usuarios["NOMBRE COMPLETO"][i])
cantPrestamos.append(var)
plt.barh(usuarioPrestamo, cantPrestamos)
plt.title("Cantidad de prestamos por persona")
plt.ylabel("Nombre")
plt.xlabel("Número de prestamos")
plt.show()
s = ttk.Style()
s.theme_use('clam')
s.configure('Treeview.Heading', background=COLOR_BUTTON_2)
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_2)
61
label_titulo = Label(frame, text = "LISTA DE LIBROS RESERVADOS", bg =
COLOR_FONDO_PANTALLA_2, font = (FUENTE_LETRA, FUENTE_TAM + 8,
FUENTE_WEIGHT))
label_titulo.place( x = 200, y = 30)
frame_2 = Frame(frame)
frame_2.config(width = TAM_BIBLIOTECA_APP_X_2, height=
TAM_BIBLIOTECA_APP_Y_2, bg = COLOR_FONDO_PANTALLA_2)
frame_2.place ( x = 20, y = 100)
scrollTabla = Scrollbar(frame_2)
scrollTabla.pack(side=RIGHT, fill=Y)
scrollTabla2 = Scrollbar(frame_2,orient='horizontal')
scrollTabla2.pack(side=BOTTOM, fill=X)
tablaContenido =
ttk.Treeview(frame_2,yscrollcommand=scrollTabla.set,xscrollcommand=scrollTabla2.set, height =
20, selectmode='browse')
tablaContenido.pack()
scrollTabla.config(command=tablaContenido.yview)
scrollTabla2.config(command=tablaContenido.xview)
62
tablaContenido.column("fecha",anchor=CENTER,width=105)
tablaContenido.column("fechafin",anchor=CENTER,width=105)
tablaContenido.column("estado",anchor=CENTER,width=120)
tablaContenido.heading("#0",text="",anchor=CENTER)
tablaContenido.heading("id",text="Id",anchor=CENTER)
tablaContenido.heading("dni",text="DNI",anchor=CENTER)
tablaContenido.heading("Codigo",text="Codigo",anchor=CENTER)
tablaContenido.heading("Titulo",text="Titulo",anchor=CENTER)
tablaContenido.heading("fecha",text="F.Prestamo",anchor=CENTER)
tablaContenido.heading("fechafin",text="F. Fin",anchor=CENTER)
tablaContenido.heading("estado",text="Estado",anchor=CENTER)
actualizarTabla()
tablaContenido.pack()
return frame
def obtenerCodigo(event=""):
cadena = comboTipo.get()
if cadena == values[0]:
cadena = "NO-"
elif cadena == values[1]:
cadena = "CU-"
elif cadena == values[2]:
cadena = "CF-"
63
elif cadena == values[3]:
cadena = "TE-"
elif cadena == values[4]:
cadena = "AC-"
elif cadena == values[5]:
cadena = "CO-"
else:
print("ERROR: No se puede crear")
return
indice = 0
for i in libros.index:
if cadena in libros["codigo"][i]:
indice += 1
codigoGenerado.set(cadena+str(indice + 1))
def addLibro():
if(texto.get() != "" and rutaImagen != ""):
global libros
fila = len(libros.index) + 1
col_nombre = 1
col_codigo = 2
col_tipo = 3
col_dispo = 4
col_prestamo = 5
64
celda.value = 0
destino = ruta+"/"+bookfolders+"/"+codigoGenerado.get()
shutil.copy(rutaImagen.get(), destino)
var1 = str(texto.get())
var2 = str(codigoGenerado.get())
var3 = str(comboTipo.get())
var4 = str(comboCantidad.get())
data = {"nombre":var1,"codigo":var2,"tipo":var3,"disponibilidad":var4}
libros = libros.append(data, ignore_index = True)
archivo.save(datafile)
fun_ir_a_frameBienvenida()
mostrarNotificacion("Creación correcta","Se ha creado el libro correctamente.")
else:
errorLabel.set("El nombre y la imagen no pueden estar vacíos")
def callback():
ruta_2=filedialog.askopenfilename()
img=Image.open(ruta_2)
rutaImagen.set(ruta_2)
o_size=img.size
f_size=(200,400)
factor = min(float(f_size[1])/o_size[1], float(f_size[0])/o_size[0])
width = int(o_size[0] * factor)
height = int(o_size[1] * factor)
rImg= img.resize((width, height), Image.ANTIALIAS)
img2 = ImageTk.PhotoImage(rImg)
panel.configure(image=img2)
panel.image = img2
rutaImagen = StringVar()
frame = Frame(ventanaContenedor)
frame.config(width = TAM_BIBLIOTECA_APP_X_2, height= TAM_BIBLIOTECA_APP_Y_2,
bg = COLOR_FONDO_PANTALLA_2)
65
button_retroceder = Button(frame,font = FUENTE, text = "RETROCEDER", bg =
COLOR_BUTTON_2, command = fun_ir_a_frameBienvenida)
button_retroceder.place( x = 20, y = 30)
pos_x_label = 50
pos_y_label = 150
aumento_y_label = 60
pos_x_label_text = 240
imagen = ImageTk.PhotoImage(Image.open(resourcesfolder+"nofile.png"))
panel = Label(frame, image=imagen)
panel.place(x= 530, y = 150)
button_cargarimagen=Button(frame,text="Cargar
Imagen",command=callback,width=15).place(x=560,y=400)
#CATEGORÍA
label_usuario = Label(frame, text = "Categoría:", bg = COLOR_FONDO_PANTALLA_2, font =
(FUENTE_LETRA, FUENTE_TAM + 6, FUENTE_WEIGHT))
label_usuario.place(x = pos_x_label , y = pos_y_label + aumento_y_label * 0)
values = generosLibros
comboTipo = ttk.Combobox(frame, state="readonly", values = values, font = (FUENTE_LETRA,
FUENTE_TAM + 2, FUENTE_WEIGHT))
comboTipo.set(values[0])
comboTipo.bind("<<ComboboxSelected>>" ,obtenerCodigo)
comboTipo.place(x = pos_x_label_text , y = pos_y_label + aumento_y_label * 0)
#NOMBRE
66
label_nombre = Label(frame, text = "Titulo:", bg = COLOR_FONDO_PANTALLA_2, font =
(FUENTE_LETRA, FUENTE_TAM + 6, FUENTE_WEIGHT))
label_nombre.place(x = pos_x_label , y = pos_y_label + aumento_y_label * 1)
errorLabel = StringVar()
error = Label(frame, textvariable = errorLabel, bg = COLOR_FONDO_PANTALLA_2, fg="red")
error.place(x = pos_x_label_text , y = pos_y_label + aumento_y_label * 1 + 30)
texto = StringVar()
entry_nombre = Entry(frame, textvariable = texto, font = (FUENTE_LETRA, FUENTE_TAM + 2,
FUENTE_WEIGHT))
entry_nombre.place(x = pos_x_label_text , y = pos_y_label + aumento_y_label * 1)
#CANTIDAD
label_cantidad = Label(frame, text = "Cantidad: ", bg = COLOR_FONDO_PANTALLA_2, font =
(FUENTE_LETRA, FUENTE_TAM + 6, FUENTE_WEIGHT))
label_cantidad.place(x = pos_x_label , y = pos_y_label + aumento_y_label * 2)
#CÓDIGO
label_codigo = Label(frame, text = "Codigo auto:", bg = COLOR_FONDO_PANTALLA_2, font =
(FUENTE_LETRA, FUENTE_TAM + 6, FUENTE_WEIGHT))
label_codigo.place(x = pos_x_label , y = pos_y_label + aumento_y_label * 3)
codigoGenerado = StringVar()
67
button_registrarPrestamo = Button(frame,font = FUENTE, text = "AÑADIR LIBRO", width = 70,
height = 3, bg = COLOR_BUTTON_2, command = addLibro)
button_registrarPrestamo.place( x = 50, y = 450)
obtenerCodigo()
return frame
####################################################################
archivo = load_workbook(datafile)
hoja_libros = generarHoja("Libros")
libros = generarPandas(hoja_libros)
hoja_usuarios = generarHoja("Usuarios")
usuarios = generarPandas(hoja_usuarios)
usuarios[["DNI","CONTRASEÑA"]] = usuarios[["DNI","CONTRASEÑA"]].astype(str)
hoja_prestamos = generarHoja("Prestamos")
prestamos = generarPandas(hoja_prestamos)
prestamos[["DNI"]] = prestamos[["DNI"]].astype(str)
usuario_autorizado = ""
frame_global = ""
generosLibros = ["NOVELA","CUENTO","CIENCIA
FICCION","TERROR","ACCION","COMEDIA"]
## CONSTANTES DE COLORES
COLOR_FONDO_PANTALLA_1 = "#CDF0EA"
COLOR_FONDO_PANTALLA_2 = "#ecb0ee"
COLOR_ENTRY_1 = "#DEEDF0"
COLOR_ENTRY_2 = "#"
COLOR_BUTTON_1 = "#98DDCA"
COLOR_BUTTON_2 = "#c67dc8"
## CONSTANTE DE LETRA
68
FUENTE_LETRA = "Cambria"
FUENTE_TAM = 11
FUENTE_WEIGHT = "bold"
## CONSTANTES DE TAMAÑO
TAM_BIBLIOTECA_APP_Y = 400
TAM_BIBLIOTECA_APP_X = 500
TAM_BIBLIOTECA_APP = str(TAM_BIBLIOTECA_APP_Y) + "x" +
str(TAM_BIBLIOTECA_APP_X)
TAM_BIBLIOTECA_APP_Y_2 = 600
TAM_BIBLIOTECA_APP_X_2 = 800
TAM_BIBLIOTECA_APP_2 = str(TAM_BIBLIOTECA_APP_X_2) + "x" +
str(TAM_BIBLIOTECA_APP_Y_2)
# PRINCIPAL
bibliotecaApp = crearPantallaInicial()
# FUENTE A UTILIZAR
FUENTE = tkFont.Font(family=FUENTE_LETRA, size=FUENTE_TAM,
weight=FUENTE_WEIGHT)
# IMAGENES LOGIN
click_login = PhotoImage(file = resourcesfolder+"logo.png")
# IMAGENES FRAME BIENVENIDA
click_reservar = PhotoImage(file = resourcesfolder+"reservar.png")
click_busqueda = PhotoImage(file = resourcesfolder+"busqueda.png")
click_devolver = PhotoImage(file = resourcesfolder+"devolver.png")
# IMAGENES FRAME RESERVARS
click_novelas = PhotoImage(file = resourcesfolder+"novelas.png")
click_cuentos = PhotoImage(file = resourcesfolder+"cuentos.png")
click_cienciaficcion = PhotoImage(file = resourcesfolder+"cienciaficcion.png")
# IMAGENES FRAME BIENVENIDA ADMIN
click_addlibro = PhotoImage(file = resourcesfolder+"addLibro.png")
click_reportes = PhotoImage(file = resourcesfolder+"reporteReserva.png")
frame_login = crearFrmLogin(bibliotecaApp)
69
frame_createUser = ventanaRegistroUsuario(bibliotecaApp)
frame_login.pack()
# FRAME USUARIO
frame_bienvenida = crearFrameBievenida(bibliotecaApp)
frame_reservar = crearFrameReservar(bibliotecaApp)
frame_busqueda = ""
frame_retornar = ""
# FRAME ADMIN
frame_bienvenida_adm = crearFrameBienvenidaAdmin(bibliotecaApp)
frame_reporte = crearFrameReportes(bibliotecaApp)
frame_addlibro = crearFrameAddLibro(bibliotecaApp)
bibliotecaApp.mainloop()
70