main.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. import datetime
  2. import logging
  3. import queue
  4. import signal
  5. import threading
  6. import time
  7. import tkinter as tk
  8. from logging.handlers import RotatingFileHandler
  9. from tkinter import (HORIZONTAL, VERTICAL, E, N, S, W, filedialog, messagebox,
  10. ttk)
  11. from tkinter.scrolledtext import ScrolledText
  12. from PIL import Image, ImageTk
  13. from qhandler import QueueHandler
  14. from simsdt import runsdt
  15. fh = RotatingFileHandler(
  16. 'log/simsdt_app_monitor.log', maxBytes=1024*50, backupCount=10)
  17. fmt = logging.Formatter(
  18. '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
  19. fh.setFormatter(fmt)
  20. fh.setLevel(logging.INFO)
  21. # logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  22. # datefmt='%Y-%m-%dT%H:%M:%S',
  23. # handlers=[RotatingFileHandler(
  24. # 'log/simsdt_app_monitor.log', maxBytes=10240, backupCount=10)],
  25. # level=logging.INFO)
  26. main_logger = logging.getLogger("simsdt")
  27. main_logger.addHandler(fh)
  28. main_logger.setLevel(logging.DEBUG)
  29. class RunsdtThread(threading.Thread):
  30. def __init__(self, group=None, target=None, name=None,
  31. args=(), kwargs=None, *, daemon=None):
  32. super(RunsdtThread, self).__init__(group=group,
  33. target=target, name=name, daemon=daemon)
  34. self._stop_event = threading.Event()
  35. self.args = args
  36. self.kwargs = kwargs
  37. return
  38. def run(self):
  39. now = datetime.datetime.now()
  40. runsdt.main(self.args)
  41. # time.sleep(30)
  42. main_logger.info('Fin de la función run')
  43. def stop(self):
  44. self._stop_event.set()
  45. class Clock(threading.Thread):
  46. """Class to display the time every seconds
  47. Every 5 seconds, the time is displayed using the logging.ERROR level
  48. to show that different colors are associated to the log levels
  49. """
  50. def __init__(self):
  51. super().__init__()
  52. self._stop_event = threading.Event()
  53. def run(self):
  54. # main_logger.setLevel(logging.INFO)
  55. main_logger.debug('Clock started')
  56. previous = -1
  57. while not self._stop_event.is_set():
  58. now = datetime.datetime.now()
  59. if previous != now.second:
  60. previous = now.second
  61. if now.second % 5 == 0:
  62. level = logging.ERROR
  63. else:
  64. level = logging.INFO
  65. main_logger.log(level, now)
  66. time.sleep(0.2)
  67. def stop(self):
  68. self._stop_event.set()
  69. class ConsoleUi:
  70. """Poll messages from a logging queue and display them in a scrolled text widget"""
  71. def __init__(self, frame):
  72. self.frame = frame
  73. # Create a ScrolledText wdiget
  74. self.scrolled_text = ScrolledText(frame, state='disabled', height=25)
  75. self.scrolled_text.grid(row=0, column=0, sticky=(N, S, W, E))
  76. # Font config
  77. self.scrolled_text.configure(font='TkFixedFont')
  78. self.scrolled_text.tag_config('INFO', foreground='black')
  79. self.scrolled_text.tag_config('DEBUG', foreground='gray')
  80. self.scrolled_text.tag_config('WARNING', foreground='orange')
  81. self.scrolled_text.tag_config('ERROR', foreground='red')
  82. self.scrolled_text.tag_config(
  83. 'CRITICAL', foreground='red', underline=1)
  84. # Create a logging handler using a queue
  85. self.log_queue = queue.Queue()
  86. self.queue_handler = QueueHandler(self.log_queue)
  87. formatter = logging.Formatter(
  88. '%(asctime)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
  89. self.queue_handler.setFormatter(formatter)
  90. # self.queue_handler.setLevel(logging.INFO)
  91. main_logger.addHandler(self.queue_handler)
  92. # Start polling messages from the queue
  93. self.frame.after(100, self.poll_log_queue)
  94. def display(self, record):
  95. msg = self.queue_handler.format(record)
  96. self.scrolled_text.configure(state='normal')
  97. self.scrolled_text.insert(tk.END, msg + '\n', record.levelname)
  98. self.scrolled_text.configure(state='disabled')
  99. # Autoscroll to the bottom
  100. self.scrolled_text.yview(tk.END)
  101. def poll_log_queue(self):
  102. # Check every 100ms if there is a new message in the queue to display
  103. while True:
  104. try:
  105. record = self.log_queue.get(block=False)
  106. except queue.Empty:
  107. break
  108. else:
  109. self.display(record)
  110. self.frame.after(100, self.poll_log_queue)
  111. class FormUi:
  112. runsdt_thread = None
  113. def __init__(self, frame, q):
  114. self.frame = frame
  115. self.q = q
  116. image = Image.open('img/play.png')
  117. self.photo = ImageTk.PhotoImage(image)
  118. # Add a text field for file path
  119. self.file_path = tk.StringVar()
  120. ttk.Label(self.frame, text='Archivo:').grid(column=0, row=0, sticky=W)
  121. ttk.Entry(self.frame, textvariable=self.file_path,
  122. width=50).grid(column=1, row=0, sticky=(W, E))
  123. self.button = ttk.Button(
  124. self.frame, text='...', command=self.open_file_dialog, width=3)
  125. self.button.grid(column=2, row=0, sticky=W)
  126. self.button_run = ttk.Button(self.frame, image=self.photo, compound="right", text='Ejecutar',
  127. command=self.run)
  128. self.button_run.grid(column=1, row=1, sticky=W)
  129. def open_file_dialog(self):
  130. # Open file dialog
  131. file = filedialog.askopenfilename(
  132. filetypes=[('Excel', '.xlsx')], title='Seleccione un archivo de subasta...')
  133. main_logger.info(f'Archivo seleccionado: {file}')
  134. self.file_path.set(file)
  135. def run(self):
  136. if not self.file_path.get():
  137. main_logger.error('Debe seleccionar un archivo para ejecutarlo')
  138. return
  139. self.button_run.configure(state='disabled')
  140. main_logger.info('Ejecutando')
  141. # self.runsdt_thread = threading.Thread(
  142. # target=runsdt.main, args=[self.file_path.get()])
  143. # self.runsdt_thread.start()
  144. self.runsdt_thread = RunsdtThread(args=(self.file_path.get()))
  145. self.runsdt_thread.start()
  146. self.check_thread()
  147. def check_thread(self):
  148. if not self.runsdt_thread.is_alive():
  149. self.button_run.configure(state='normal')
  150. self.q.put(False)
  151. else:
  152. self.q.put(True)
  153. self.frame.after(100, self.check_thread)
  154. class ThirdUi:
  155. def __init__(self, frame, q):
  156. self.q = q
  157. self.frame = frame
  158. self.pb_max = 100
  159. # ttk.Label(self.frame, text='This is just an example of a third frame').grid(
  160. # column=0, row=1, sticky=W)
  161. self.progress_bar = ttk.Progressbar(
  162. self.frame, mode='determinate')
  163. self.progress_bar.pack(expand=True, fill=tk.BOTH, side=tk.TOP)
  164. self.frame.after(100, self.check_q)
  165. def check_q(self):
  166. # Check every 100ms if there is a new message in the queue to display
  167. while True:
  168. try:
  169. record = self.q.get(block=False)
  170. except queue.Empty:
  171. break
  172. else:
  173. if record:
  174. step = 1
  175. self.progress_bar.step(step)
  176. if step >= self.pb_max:
  177. step = 1
  178. else:
  179. step += 1
  180. else:
  181. self.progress_bar.config(value=0)
  182. self.frame.after(100, self.check_q)
  183. # max_size = 100
  184. # print(self.q.qsize())
  185. # while self.q.qsize():
  186. # try:
  187. # active_tread = self.q.get(0)
  188. # print(active_tread)
  189. # main_logger.info(active_tread)
  190. # if active_tread:
  191. # step = 1
  192. # self.progress_bar.step(step)
  193. # if step == max_size:
  194. # step = 1
  195. # else:
  196. # step += 1
  197. # except queue.Empty:
  198. # pass
  199. # self.frame.after(100, self.check_q)
  200. class App:
  201. def __init__(self, root):
  202. self.root = root
  203. pbqueue = queue.Queue()
  204. root.title('SimSDT')
  205. root.iconbitmap("app.ico")
  206. root.columnconfigure(0, weight=1)
  207. root.rowconfigure(0, weight=1)
  208. # Create the panes and frames
  209. vertical_pane = ttk.PanedWindow(self.root, orient=VERTICAL)
  210. vertical_pane.grid(row=0, column=0, sticky="nsew")
  211. horizontal_pane = ttk.PanedWindow(vertical_pane, orient=HORIZONTAL)
  212. vertical_pane.add(horizontal_pane)
  213. form_frame = ttk.Labelframe(horizontal_pane, text="Configuracion")
  214. form_frame.columnconfigure(1, weight=1)
  215. horizontal_pane.add(form_frame, weight=1)
  216. console_frame = ttk.Labelframe(horizontal_pane, text="Console")
  217. console_frame.columnconfigure(0, weight=1)
  218. console_frame.rowconfigure(0, weight=1)
  219. horizontal_pane.add(console_frame, weight=1)
  220. third_frame = ttk.Labelframe(vertical_pane, text="Estado")
  221. vertical_pane.add(third_frame, weight=1)
  222. # Initialize all frames
  223. self.form = FormUi(form_frame, pbqueue)
  224. self.console = ConsoleUi(console_frame)
  225. self.third = ThirdUi(third_frame, pbqueue)
  226. self.root.protocol('WM_DELETE_WINDOW', self.quit)
  227. self.root.bind('<Control-q>', self.quit)
  228. signal.signal(signal.SIGINT, self.quit)
  229. def quit(self, *args):
  230. msg = 'Se esta ejecutando un proceso de optimización\n\n¿Desea salir de la aplicación?'
  231. if self.form.runsdt_thread and self.form.runsdt_thread.is_alive():
  232. if messagebox.askokcancel("Salir de SimSDT", msg):
  233. main_logger.info('Cerrando aplicacion SimSDT')
  234. self.form.runsdt_thread.stop()
  235. self.root.destroy()
  236. else:
  237. main_logger.info('Saliendo de la aplicacion')
  238. self.root.destroy()
  239. def main():
  240. main_logger.info('SimSDT ha iniciado')
  241. root = tk.Tk()
  242. app = App(root)
  243. app.root.mainloop()
  244. if __name__ == '__main__':
  245. main()