From 2fe7d676bb97c6cb7b7510b29accbb43e753dbdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moran?= Date: Sat, 15 Feb 2025 17:50:46 +0100 Subject: [PATCH] Limpieza de codigo y correo finalizado --- app/__pycache__/emailClient.cpython-313.pyc | Bin 4187 -> 4187 bytes app/__pycache__/emailTab.cpython-313.pyc | Bin 4760 -> 5498 bytes app/__pycache__/game.cpython-313.pyc | Bin 6937 -> 6917 bytes app/__pycache__/gestor_tareas.cpython-313.pyc | Bin 3960 -> 3970 bytes app/__pycache__/graphics.cpython-313.pyc | Bin 4130 -> 4283 bytes .../monitorization.cpython-313.pyc | Bin 4635 -> 4606 bytes app/__pycache__/panel_derecho.cpython-313.pyc | Bin 9189 -> 9189 bytes .../panel_izquierdo.cpython-313.pyc | Bin 6995 -> 6970 bytes app/__pycache__/scraping.cpython-313.pyc | Bin 3939 -> 4103 bytes app/__pycache__/todo_list.cpython-313.pyc | Bin 4541 -> 6234 bytes .../__pycache__/emailClient.cpython-313.pyc | Bin 0 -> 7873 bytes .../__pycache__/emailTab.cpython-313.pyc | Bin 0 -> 5026 bytes .../reciveEmailTab.cpython-313.pyc | Bin 0 -> 7354 bytes app/email/emailClient.py | 133 ++++++++++++++++++ app/email/emailTab.py | 70 +++++++++ app/email/reciveEmailTab.py | 106 ++++++++++++++ app/emailClient.py | 59 -------- app/emailTab.py | 68 --------- app/game.py | 2 - app/gestor_tareas.py | 2 - app/graphics.py | 5 +- app/monitorization.py | 2 - .../__pycache__/chat.cpython-313.pyc | Bin 0 -> 3796 bytes .../__pycache__/panel_derecho.cpython-313.pyc | Bin 0 -> 13191 bytes app/{ => panel_derecho}/chat.py | 9 +- app/{ => panel_derecho}/panel_derecho.py | 85 ++++++++++- .../panel_izquierdo.cpython-313.pyc | Bin 0 -> 7367 bytes app/{ => panel_izquierdo}/panel_izquierdo.py | 11 +- app/scraping.py | 6 +- app/todo_list.py | 98 ++++++++----- main.py | 94 ++++++------- 31 files changed, 516 insertions(+), 234 deletions(-) create mode 100644 app/email/__pycache__/emailClient.cpython-313.pyc create mode 100644 app/email/__pycache__/emailTab.cpython-313.pyc create mode 100644 app/email/__pycache__/reciveEmailTab.cpython-313.pyc create mode 100644 app/email/emailClient.py create mode 100644 app/email/emailTab.py create mode 100644 app/email/reciveEmailTab.py delete mode 100644 app/emailClient.py delete mode 100644 app/emailTab.py create mode 100644 app/panel_derecho/__pycache__/chat.cpython-313.pyc create mode 100644 app/panel_derecho/__pycache__/panel_derecho.cpython-313.pyc rename app/{ => panel_derecho}/chat.py (84%) rename app/{ => panel_derecho}/panel_derecho.py (62%) create mode 100644 app/panel_izquierdo/__pycache__/panel_izquierdo.cpython-313.pyc rename app/{ => panel_izquierdo}/panel_izquierdo.py (93%) diff --git a/app/__pycache__/emailClient.cpython-313.pyc b/app/__pycache__/emailClient.cpython-313.pyc index 7fba0fac82bd0c2b8af56af372f39afa0e70f060..9c7521ffe04759f3093cc81ac98288b35b60b942 100644 GIT binary patch delta 80 zcmcbua9e@*GcPX}0}xzDT9od)k=LDx@y6y*rfbZMqMLQtIvE*7Hg9A1VP#a`%*^|k ikx^~(bG{?29IRZQL?&SW diff --git a/app/__pycache__/emailTab.cpython-313.pyc b/app/__pycache__/emailTab.cpython-313.pyc index 3a44dad8001ad247772a4b867289bf7f4c92b2f6..fe807a5428ce6448359f546bb3ce41748749bdae 100644 GIT binary patch literal 5498 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 literal 4760 zcmdT|O;8-i6`uWXmRTVXLM!pNLRhd)jD-U-5S3h5U{O+_%nnpYw8?nb9mAMq26|>K z;G|qqk}E06u2RXR9JKO9P~|F<9?jxT!mW zJkL$f4KMXZgs{*iNZxaTVfaxmp~p0w+d>6UjAATCWTaycdGzeh@qfEPn$65>dlXZS;euFu2}VW#xDMWi%HI}jiU>18)HBBX*9=NkW6j09mbzp`0~D1qQAHoh3GPIS=$P+4s?4k4 zWM!Iplw^_-Q?=3ya$1YJnb%ZPA2OmFmO5=1b0+#H5@GX+f%v4UQZqhpP(_aq8Hw~f zgvE@%B@Ydb#wC@0tR*zVjHr5qs>%5K%hs%+$4ApCOM9Oh3Dq>r_iEm(UGF#K?9Qo% zNBy7me?GN!>f&ARpU5^jd0)IIKA3nk_32b*fLlQNVF~_^2&*t7kCYyG6$quERNe1}^-^3>fKNP#4SN+V`04n> ziugjW0$)i4i&+8)PaG3rNc!mrPgF!$^NJA`GaC?|JSM`LSAg(jMTE70u~(-$^(S;hyumGg7$Ub70;7 z7|ds+vw6(LlL%}$S8l_R`p<#iL)`C;hjzaogSrVcHkE6vh`M%Z$-!Wf_!M~WLgbE&(ywMKo+LygOEQM;uz%FWC2@INP z-GUw-K`Ufb)lKCibzqkt;BH;r?F2rY&Hh-SP`p23=BMzphn_^|b z0tsV&UeS}gh$`wSF%WIry#j>d<`ebpzhXfLsJWo%`#4r=VroL0)(RTTT|Tddxve?o z`;{`SrYLS*%sWH1B=Zde;$>D1RRLX=`DxBhIGqK>G;bR;$?BYbvcv~aq3{U`_W|=x zK=HyvEMa6QFwhaq|8Cl{44s8k4p>g`N+&6omBI>zSTnrkjv3&xokrwBa-l;SQ_ zQcx7C9!zO-2_kVv_$LY8C!df9^^d}zhO?2&HhDWouGr+t^V-es?BrBd{vbOun?;tk z)7bo^?{Qyd=-J5Ak*&rzunq}Bjgcc^n}l|H$|J#)e)Z{|pk zO?ozJpLcIc*x&zU zS?mq|$1hY>?^~a#vB{+z>9tAk#`))in~ChyZ?YdKS>#Obw6#C${GxNU=c~T8zOA+% zl-|mvx7wsNN7`-DzFNE9z0v)=JBz*HgSJ=MgOUw3>Cucz&%!5^0Ic&6%;s%`P){gHbk+qIGH`f#rPqFsOS-vMt` z@OvUu*W7D=(Ea&%CU&>o4tBzn=GGEbu?=cBy<15Yx(V_F!HkwGYval~?fTB6l>H;v zNpTxIP>H84tO<){;VmRW^10#>9Opn@?3-maH8ON;R zN~}6=Rph!=tS(|loN0wCX6*F7t%X)WTW`Le=65Og*ovpS>r%9uqRkX<8uTq7%rm1} z^cUFeL4uR%+eo^AFwgMV5c8Q9)fOmjvZWgj%3uw}o6Lc2>@tr11CTpH$s*~nNe5(W zPHeKprkohD#mJV}#yPFF*qReB*y4pP@iorDc24ZD#f~j8$~n!p*qjs3+v52x@xmca z=Cv>DzG_%&*b*=0#LKpLc}whjS?#Y1mZu@Qg{R@)pz;5ahQ0Yk!9(d9i~5}?{t^K2 zDAISZ#iKZYt;3b|@^5Nn`9qDGR!Y-V-H>I9We-34 zoK)rjPs_4tDNxkNiUk=vowig{me&MslXF<&6^tUmuXzT@AB7iguP^*U?C?cj#9RSi z@MWFP=iuNYqka~!<}}?>DPI%L-wiczx$olTfb&ld|6hWSr#DasZhl-PBUc9?g<#K%|)PF0S`Bv!o-h0&*d?5hgGye-0Pf;fT diff --git a/app/__pycache__/game.cpython-313.pyc b/app/__pycache__/game.cpython-313.pyc index 29607534ff448f086281ee4f9cec24f7bd534221..b8d9c6ca1dabf915fd06079a95465fe901565442 100644 GIT binary patch delta 260 zcmbPf)@sK4nU|M~0SJ1+*QKwY$ScX{F;QJXlgXRei=~Lgi?xW=i>-*wi@k_lfkA>n zi6NL-iXoWAlnJDTfuV?FV#7*Cw#{0Ms!WWTlLMH|d3{TBN-_hA@{?1G^NT09F*`G| zPd>$bpHY1BDwh9@5|dZ4{$Z4#yqs+hqxj|&_UTNFN}K<1E@NZV*}PaFn1#`I^LLRf zCdSy!N#b)D88asTmsrM_J9(MpTt>FZE>f_~o#2m~J%;?Qh#0sRDyji^1irBo^ zi`cz5ia5MDi#Qb+Bp8$!f|;clf>}(NKt?by6md-~Sjot-S&C7Wi7|Sz1GD+$R%Rzg z&dDd3?=wnHUcvI8QEKuE)<2Ajlb5pXVU*mQ#6F#gQDyTl&Sh+jdYcyt1hX*uZ~iKh z#l#rDIYE35BV*R&zY@zB^CvHnoXf~D*-6S(mHQT3L1lVkZfa39$VO)nVFV;@v6p0L w=9Q!t-C`=qp1eTHSg{Hy$_T{8YCz%xGb1D8Z3f=k3<6-1`!<8fK%&SBXfOa| C9U1=s diff --git a/app/__pycache__/graphics.cpython-313.pyc b/app/__pycache__/graphics.cpython-313.pyc index f983f7ac7fd3f1d08f2b1926cf28561f42df8883..669e1d96598c65a752b731886b0cb6554d738d29 100644 GIT binary patch delta 1178 zcmaJ>-D?wB6u)=AGMP-$#`u}8b)vR9r5Hg(x*vgnUqyu zWMTh-a&h-%AC`Svc2|%-`m_|qhXtcAT?WNBMSM^YAJhjuXWCFj@WTAgx##1~xxaH} z{;+Q5QZJK9gW;-8U2hDVi>a+wCvkU;xyt~g&i|t+0>YTAgdhy0Fp1$!qC*^i`Bb>pmwk~%vcZ2v!K2XLeU$0nQkF&F@txJsU zaeK>piXD&+u2p5R9n4MpdQl%6U6nI@hJ7RNU^Cp#Ed3?tcsx{(IL;5w6UT!Ls)iQjb!XKEMS4``^Y5l&XGhFVRJk=>rb%su_nA7*n>9;Rr9!%ml$ifiL&wuv8JySm9e55$q$H4EgTd7$b(|qXeE+(4x|yTSj2Ci_1)nk zQ(szRumNp$8ZzYHNU%vLiCmQ`fj$DN1T2Ca0d+iT3G5<`#s&ruY!zs5U=P6%LTJ|J zT7h@=0yMEdun$dvieS-#y!a`RGYYu3$+05-PUIzriwB9Hi_XXG8oe`dr_>o5Uoj`{ znG;d79R4QQig`5>_IXUfnWd?L%?P1>d8X-C{h9|OWGfNSe5}$K^?<4a;w7p8 n=u{XdSbwqp^?a+oFy|G3elqwSvw*U2iaCGC`o*grTcPOp{cr1n delta 993 zcmZWnO-vI(6rS0icDuVR3)IMO*$N^}tRx^o`7yK&AP}lyB_XK9(gRWiGbIVI4YLR(Pw!$ zs*5z@{f>tx?_>nz)8*qV^5-PTaf5_Ns+u8Pgl#GsHP~BaoEqhN8Wyw*)$B|MyQiL` zwQN@G9d00`prpdpoK-Wc1W2#YM=KGNv=PfFC^02DQsErUkzuiod?bb`ljR;- z&#uWYya)0_$QMfNlbodsEaB;APd&HPEBq)l$C+neJl!-=uJzW_<^(;|Yj=agTj?!p zw=TA?$M^L3?_-hr5^O^0HoW4y?!cHJz8B;%ub2^-=?@A&D0`{2QVn&9T8OYlYO+q> zPpzdYU?zm(=JhiH(;o;4lN1+PSzeMx(Ne0p8nM(_=|9n0L5fMqKFj5QMEh69*=Mnn z?Wn~;ou9B89t9|ZnO-)F_e*xc1~Z#36&*8cFPjtb(YXOL2?j5W7*NXz>JaMLsusSB zJwYQv6G8}~4Z%dfO98w!Xy*`-p#$G9B6M*$&@;-+k^o<86jKlsbRj>>U4VUTS@P4tM{O@TyI z+>XkLo^q?do${%P1>)M5_3=Z=x2v{%lv>>nBN_yG_CR@@%NNpx zj1B#K3o!(ofr?-5@32Qe17PwhA|4OZ2xlEALl27Cm1X+|+~x_Kc?Ub=Q%EU$W<(?` F{R@k5$VLDF diff --git a/app/__pycache__/monitorization.cpython-313.pyc b/app/__pycache__/monitorization.cpython-313.pyc index e99ef99d4d2a6e7bec667856b9d7d180ebf1f6a1..0facb1c5caf0c5d76c22bebbc7f6d61d71f726ba 100644 GIT binary patch delta 185 zcmbQO@=uxXGcPX}0}$+tSeO2tcOstzqs2sZC2?jch9Z_=79hzQ%nBsgg4uv1`^JK9 zM#kdFe;CbqeM@snG6Rb8lT(ZHizgc~xihkEu3}5M$lNo c1;}6o;$jsb@qw9$(yB!B^X4r7O@61OEDC&1+xH2 z_Fz^Z$q~#3Bsn)GbTcxhPJY8^KG~4TjgftG1rsY1qrm1mmX|DyCX+KbpD@}^_T}2i z=roy~`yiw1=9ApwjEvHoAM$)+X7rqVMPRKa*Ddyv?99B9)S@D0pgV4HmShyACZ=TO zr5Evm_#7#TC8;Huxv7(<3L49~0fiWWxL6BFd|+l|WW38Dcbh@rHUsx<2L8zp1mys7 C{Wx9# diff --git a/app/__pycache__/panel_derecho.cpython-313.pyc b/app/__pycache__/panel_derecho.cpython-313.pyc index 86e2c7166576e776cee2f23285892ae7199868dc..4fe1c292378ad7539a2e7568d339c0e942c4daa4 100644 GIT binary patch delta 20 acmaFr{?wiOGcPX}0}u#2S-g?^mNEcJg$Bz2 delta 20 acmaFr{?wiOGcPX}0}#9lp1G0xmNEcL;|B@= diff --git a/app/__pycache__/panel_izquierdo.cpython-313.pyc b/app/__pycache__/panel_izquierdo.cpython-313.pyc index b9542a7278e6c6d573c6cbc7fee0db2f51e0e9c3..2aa786a6998af1b690cbc26d5c82cf7a66cbd6cb 100644 GIT binary patch delta 747 zcmYk3Pi)dq9LM|G2Q3Irh^)YHpvg1@gc6e_+i>C^Vz_KEO*QIf>9&rcu8`MGbcu1E z=S5%i)J!~N!^hk^c!`=9PxEX#)KmEP;`~Ld-)34(n$Ai6K(9bd3 zl-sqRkAr_=Je>9nA_9AQ&oIZ)hMBk=!fJFt=+Qj1B%Gi}@Gy(S)OegzN7Tdww!E5`Mdk%j6Z=KBu@Vy1 z(Sck^lc0#`PJ7-V&k}3xDcuepr3o=bOXApAnqMDYah#?-na{Uct(>uHtT(KjY3DmW zb&(j&X5FgM`{JzF=6WzB{UN@IrkS(BJ+JH}vR@L}Z*p=+E`FAayY#gb;fdmYl9I^h z>tNfwy-cQP&(|38v6R%lkpdL5}8+eaVnZ5{}^Il*HINyeGtn|i5_UI39LD{=9)R?JUvFbqWd)*`OtII$!u=hT>9KgRa0xEg z01p{D5nZ<(!>KRohU1X>!kS~+x=!*SfX}c=AH}n&z6eO?^lOjsgK6 z$DTI%ymfZl-WhW)>AZdvWok8?a8j-JX1H8r$@|%$mZ3xTl zU0e)LtVI@V`=&LNOqR>#L~bRwRx%TYm87>lm%}ks_1ng(oh>isi-v6>CRU;Qp6&BV zW*t|!Cl?RediL9Tz9^jsO8UK${uoJY`}e}>A0lqA|1EJ1p@+ss@z8_YBS)O`Qtm|c zzN%mu%LOvTBuIjO@|GImy3y!d^yHBwz2=LH<2-BO-1iv>{iNUgUs1oaTlZQ(WAt8d zL>h(aX}TNim2@z#ZZ?f77no($xneb~XtY9-&UDiobgrBISEYQ;SSgtkTbP`MvN)iZ zA;uG^CM5=ob>`-xY>x3z7WLEJx>ctmZ18A`-c>SpW+3l6;09opp(-qu7ELk^eio1i z7z{C)6u>S5767XZRZouCi}TAy)oU7MD+@;^CPdv8WM14fLdi4E;0%=ECJ`W=#e9Ybp1wjxm=r<$t6{)(PXZr(#f&_ufI>ao z#)03EC|wi$nVKU9&$^iYLT47Cwv}0pUqBZ$s!)7$!~Et63I4hZxvXHY~QqQm#Pbvr?AKb zuz(!kD266#lc1DCA+jVj`-doHE8E}66X|{;w5N9Ls2%J6TK~+hdikf?vZv}hs=gko z^-p|O_R@3P>ABj(wOZfxUG)YT^bPANnfo|+lH#ZC9*SFXG?f&#dV{GEVQU1Zk{b_w zNuj`ccv9X4+XByLu9$h(F`?^FwJMd8Q=bePsRG9s9A_TRmo3|jVHjXiGlSC%1{tvL zv@r#p9SD(lAcDsz@7uT-nBwJO8pdS6`?%2!aMBb`a&AekleFYCM zyQXUbn-19SaMT~tl)mBM0#id2JbA2Q72L9!glS3^_!fgmA-zHw19+R&k#QBYgG F<`0S%#47** delta 780 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 lCr44gM`d52QuBZDZ&AZ(zS}!5-0dE- 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 literal 0 HcmV?d00001 diff --git a/app/email/emailClient.py b/app/email/emailClient.py new file mode 100644 index 0000000..5d48c58 --- /dev/null +++ b/app/email/emailClient.py @@ -0,0 +1,133 @@ +# emailClient.py +import datetime +import smtplib +import imaplib +import email +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +import threading +import traceback + +class EmailClient: + def __init__(self): + self.server_ip = "s1.ieslamar.org" + self.ports = { + "SMTP": 25, + "SMTP-S": 465, + "SMTP-SUBMISSION": 587, + "IMAP": 143, + "IMAP-S": 993 + } + + def enviar_correo(self, email_user, password, destinatario, asunto, mensaje): + def enviar(): + try: + with smtplib.SMTP(self.server_ip, self.ports["SMTP"], local_hostname="localhost") as smtp: + smtp.login(email_user, password) + msg = MIMEMultipart() + msg["From"] = email_user + msg["To"] = destinatario + msg["Subject"] = asunto + msg["Date"] = datetime.datetime.now().strftime("%a, %d %b %Y %H:%M:%S %z") + msg.attach(MIMEText(mensaje, "plain")) + + smtp.sendmail(email_user, destinatario, msg.as_string()) + print("Correo enviado correctamente.") + except Exception as e: + print(f"Error al enviar el correo: {e}") + traceback.print_exc() + + thread = threading.Thread(target=enviar) + thread.start() + + def recibir_correos(self, email_user, password, callback): + """Recupera todos los correos (leídos y no leídos) del buzón de entrada.""" + def recibir(): + try: + with imaplib.IMAP4_SSL(self.server_ip, self.ports["IMAP-S"]) as mail: + mail.login(email_user, password) + mail.select("inbox") + + status, mensajes = mail.search(None, 'ALL') # Obtener todos los correos + lista_mensajes = mensajes[0].split() + + correos = [] + for num in lista_mensajes: + # Obtener el mensaje completo junto con sus FLAGS + status, data = mail.fetch(num, '(RFC822 FLAGS)') + if not data or len(data) < 2 or not isinstance(data[0], tuple): + continue # Evitar errores si no hay datos válidos + + mensaje_bytes = data[0][1] + mensaje = email.message_from_bytes(mensaje_bytes) + + # Obtener las banderas del mensaje + status, flag_data = mail.fetch(num, '(FLAGS)') + flags = flag_data[0].decode() if flag_data and flag_data[0] else "" + + # Determinar si está leído o no + leido = '\\Seen' in flags + + correos.append({ + "id": num.decode(), + "from": mensaje["From"], + "subject": mensaje["Subject"], + "date": mensaje["Date"], + "read": leido + }) + + callback(correos) # Enviar la lista de correos a la GUI + except Exception as e: + print(f"Error al recibir correos: {e}") + + threading.Thread(target=recibir).start() + + + def eliminar_correo(self, email_user, password, correo_id): + def eliminar(): + try: + with imaplib.IMAP4_SSL(self.server_ip, self.ports["IMAP-S"]) as mail: + mail.login(email_user, password) + mail.select("inbox") + + mail.store(correo_id, "+FLAGS", "\\Deleted") + mail.expunge() + except Exception as e: + print(f"Error al eliminar el correo: {e}") + + threading.Thread(target=eliminar).start() + + + def obtener_contenido_correo(self, email_user, password, correo_id, callback): + def obtener(): + try: + with imaplib.IMAP4_SSL(self.server_ip, self.ports["IMAP-S"]) as mail: + mail.login(email_user, password) + mail.select("inbox") + + status, data = mail.fetch(correo_id, '(RFC822)') + mensaje = email.message_from_bytes(data[0][1]) + + # Obtener el cuerpo del mensaje + cuerpo = "" + if mensaje.is_multipart(): + for parte in mensaje.walk(): + if parte.get_content_type() == "text/plain": + cuerpo = parte.get_payload(decode=True).decode() + else: + cuerpo = mensaje.get_payload(decode=True).decode() + + # Marcar como leído + mail.store(correo_id, '+FLAGS', '\\Seen') + + callback({ + "from": mensaje["From"], + "subject": mensaje["Subject"], + "date": mensaje["Date"], + "body": cuerpo + }) + except Exception as e: + print(f"Error al obtener el contenido del correo: {e}") + + thread = threading.Thread(target=obtener) + thread.start() diff --git a/app/email/emailTab.py b/app/email/emailTab.py new file mode 100644 index 0000000..a2342a9 --- /dev/null +++ b/app/email/emailTab.py @@ -0,0 +1,70 @@ +import tkinter as tk +from tkinter import ttk, messagebox +from app.email.emailClient import EmailClient +import threading + +class EmailTab: + def __init__(self, notebook, panel_derecho): + self.panel_derecho = panel_derecho # Referencia al Panel Derecho + self.email_client = EmailClient() + + self.tab = ttk.Frame(notebook) + notebook.add(self.tab, text="\U0001F4E7 Correo") + self.setup_ui() + + def setup_ui(self): + """Configura la interfaz gráfica de la pestaña de correo.""" + frame_principal = tk.Frame(self.tab, bg="white", padx=10, pady=10) + frame_principal.pack(fill="both", expand=True) + frame_principal.columnconfigure(0, weight=1) + + # Enviar correo + tk.Label(frame_principal, text="✉️ Enviar Correo", font=("Helvetica", 14, "bold"), bg="white").grid(row=1, column=0, pady=5) + + frame_envio = tk.Frame(frame_principal, bg="white") + frame_envio.grid(row=2, column=0, pady=5, sticky="ew") + frame_envio.columnconfigure(1, weight=1) + + tk.Label(frame_envio, text="Para:", font=("Helvetica", 11), bg="white").grid(row=0, column=0, sticky="w") + self.entry_destinatario = tk.Entry(frame_envio, font=("Helvetica", 11)) + self.entry_destinatario.grid(row=0, column=1, sticky="ew", padx=5) + + tk.Label(frame_envio, text="Asunto:", font=("Helvetica", 11), bg="white").grid(row=1, column=0, sticky="w") + self.entry_asunto = tk.Entry(frame_envio, font=("Helvetica", 11)) + self.entry_asunto.grid(row=1, column=1, sticky="ew", padx=5) + + tk.Label(frame_envio, text="Mensaje:", font=("Helvetica", 11), bg="white").grid(row=2, column=0, sticky="w", pady=3) + self.text_mensaje = tk.Text(frame_envio, height=5, font=("Helvetica", 11)) + self.text_mensaje.grid(row=3, column=0, columnspan=2, sticky="ew", padx=5) + + # Botón de enviar + tk.Button( + frame_envio, text="📨 Enviar Correo", + font=("Helvetica", 11, "bold"), bg="green", fg="white", + command=self.enviar_correo + ).grid(row=4, column=0, columnspan=2, pady=10) + + def enviar_correo(self): + """Envía un correo en un hilo separado.""" + email, password = self.panel_derecho.get_credentials() # Obtener credenciales del panel derecho + + if not email or not password: + messagebox.showerror("Error", "⚠️ Debes iniciar sesión primero en el Panel Derecho.") + return + + def envio(): + self.email_client.enviar_correo( + email, + password, + self.entry_destinatario.get(), + self.entry_asunto.get(), + self.text_mensaje.get("1.0", tk.END).strip() + ) + messagebox.showinfo("Éxito", "✅ Correo enviado correctamente.") + self.entry_destinatario.delete(0, tk.END) + self.entry_asunto.delete(0, tk.END) + self.text_mensaje.delete("1.0", tk.END) + + threading.Thread(target=envio, daemon=True).start() + + diff --git a/app/email/reciveEmailTab.py b/app/email/reciveEmailTab.py new file mode 100644 index 0000000..c2de70b --- /dev/null +++ b/app/email/reciveEmailTab.py @@ -0,0 +1,106 @@ +import tkinter as tk +from tkinter import ttk, messagebox +from app.email.emailClient import EmailClient + +class ReciveEmailTab: + def __init__(self, notebook, panel_derecho): + self.email_client = EmailClient() + self.panel_derecho = panel_derecho + self.correos = [] + + self.tab = ttk.Frame(notebook) + notebook.add(self.tab, text="📩 Recibir Correo") + self.setup_ui() + + def setup_ui(self): + frame_principal = tk.Frame(self.tab, bg="white", padx=10, pady=10) + frame_principal.pack(fill="both", expand=True) + + # Botones de recibir y eliminar correos + frame_botones = ttk.Frame(frame_principal) + frame_botones.pack(fill="x", pady=5) + + tk.Button( + frame_botones, text="🔄 Recibir Correos", font=("Helvetica", 11, "bold"), bg="blue", fg="white", + command=self.obtener_correos_con_estado + ).pack(side="left", padx=5) + + tk.Button( + frame_botones, text="🗑️ Eliminar Correo", font=("Helvetica", 11, "bold"), bg="red", fg="white", + command=self.eliminar_correo + ).pack(side="right", padx=5) + + # Lista de correos + self.tree = ttk.Treeview(frame_principal, columns=("Leído", "De", "Asunto", "Fecha"), show="headings") + self.tree.heading("Leído", text="📌") + self.tree.heading("De", text="De") + self.tree.heading("Asunto", text="Asunto") + self.tree.heading("Fecha", text="Fecha") + self.tree.column("Leído", width=30, anchor="center") + self.tree.bind("", self.abrir_correo) + self.tree.pack(fill="both", expand=True) + + def obtener_correos_con_estado(self): + email, password = self.panel_derecho.get_credentials() + + if not email or not password: + messagebox.showerror("Error", "⚠️ Debes iniciar sesión primero.") + return + + def actualizar_lista(correos): + self.tree.delete(*self.tree.get_children()) # Limpiar la lista + self.correos = correos # Guardar la lista de correos + + for correo in correos: + estado_icono = "✅" if correo["read"] else "🔴" + self.tree.insert("", "end", values=(estado_icono, correo["from"], correo["subject"], correo["date"])) + + self.email_client.recibir_correos(email, password, actualizar_lista) + + + def abrir_correo(self, event): + email, password = self.panel_derecho.get_credentials() + + selected_item = self.tree.selection() + if not selected_item: + return + + index = self.tree.index(selected_item[0]) + correo = self.correos[index] + + def mostrar_correo(datos): + ventana = tk.Toplevel() + ventana.title("📩 Detalle del Correo") + ventana.geometry("500x400") + + tk.Label(ventana, text=f"De: {datos['from']}", font=("Helvetica", 11, "bold")).pack(pady=5) + tk.Label(ventana, text=f"Asunto: {datos['subject']}", font=("Helvetica", 11, "bold")).pack(pady=5) + tk.Label(ventana, text=f"Fecha: {datos['date']}", font=("Helvetica", 10)).pack(pady=5) + + text_area = tk.Text(ventana, wrap="word", font=("Helvetica", 11)) + text_area.insert("1.0", datos["body"]) + text_area.pack(expand=True, fill="both", padx=10, pady=10) + + # Actualizar el icono en la lista de correos + self.tree.item(selected_item[0], values=("✅", correo["from"], correo["subject"], correo["date"])) + + self.email_client.obtener_contenido_correo(email, password, correo["id"], mostrar_correo) + + def eliminar_correo(self): + email, password = self.panel_derecho.get_credentials() + + selected_item = self.tree.selection() + if not selected_item: + messagebox.showwarning("Aviso", "⚠️ Selecciona un correo para eliminar.") + return + + index = self.tree.index(selected_item[0]) + correo = self.correos[index] + + def confirmar_eliminacion(): + self.email_client.eliminar_correo(email, password, correo["id"]) + self.tree.delete(selected_item[0]) # Eliminar de la lista + + respuesta = messagebox.askyesno("Eliminar Correo", "¿Estás seguro de que deseas eliminar este correo?") + if respuesta: + confirmar_eliminacion() diff --git a/app/emailClient.py b/app/emailClient.py deleted file mode 100644 index 4b8a44c..0000000 --- a/app/emailClient.py +++ /dev/null @@ -1,59 +0,0 @@ -# emailClient.py -import smtplib -import imaplib -import email -from email.mime.text import MIMEText -from email.mime.multipart import MIMEMultipart -import threading - -class EmailClient: - def __init__(self): - self.server_ip = "192.168.120.103" - self.ports = { - "SMTP": 25, - "SMTP-S": 465, - "SMTP-SUBMISSION": 587, - "IMAP": 143, - "IMAP-S": 993 - } - - def enviar_correo(self, email_user, password, destinatario, asunto, mensaje): - def enviar(): - try: - with smtplib.SMTP(self.server_ip, self.ports["SMTP"]) as smtp: - smtp.login(email_user, password) - msg = MIMEMultipart() - msg["From"] = email_user - msg["To"] = destinatario - msg["Subject"] = asunto - msg.attach(MIMEText(mensaje, "plain")) - smtp.sendmail(email_user, destinatario, msg.as_string()) - print("Correo enviado correctamente.") - except Exception as e: - print(f"Error al enviar el correo: {e}") - - thread = threading.Thread(target=enviar) - thread.start() - - def recibir_correos(self, email_user, password, text_widget): - def recibir(): - try: - with imaplib.IMAP4_SSL(self.server_ip, self.ports["IMAP-S"]) as mail: - mail.login(email_user, password) - mail.select("inbox") - status, mensajes_no_leidos = mail.search(None, 'UNSEEN') - no_leidos = mensajes_no_leidos[0].split() - - text_widget.delete("1.0", "end") - text_widget.insert("end", f"Tienes {len(no_leidos)} correos no leídos.\n") - - for num in no_leidos: - status, data = mail.fetch(num, '(RFC822)') - mensaje = email.message_from_bytes(data[0][1]) - text_widget.insert("end", f"[NO LEÍDO] {mensaje['Subject']}\n") - mail.store(num, '+FLAGS', '\\Seen') - except Exception as e: - text_widget.insert("end", f"Error al recibir correos: {e}\n") - - thread = threading.Thread(target=recibir) - thread.start() diff --git a/app/emailTab.py b/app/emailTab.py deleted file mode 100644 index 875fea7..0000000 --- a/app/emailTab.py +++ /dev/null @@ -1,68 +0,0 @@ -# emailTab.py -import tkinter as tk -from tkinter import ttk -from app.emailClient import EmailClient - -class EmailTab: - def __init__(self, notebook): - self.email_client = EmailClient() - self.tab = ttk.Frame(notebook) - notebook.add(self.tab, text="Correo") - self.setup_ui() - - def setup_ui(self): - # Campos de entrada para credenciales - tk.Label(self.tab, text="Correo electrónico:").grid(row=0, column=0, sticky="e", padx=5, pady=5) - self.entry_email = tk.Entry(self.tab) - self.entry_email.grid(row=0, column=1, padx=5, pady=5) - - tk.Label(self.tab, text="Contraseña:").grid(row=1, column=0, sticky="e", padx=5, pady=5) - self.entry_password = tk.Entry(self.tab, show="*") - self.entry_password.grid(row=1, column=1, padx=5, pady=5) - - # Campos de entrada para enviar correo - tk.Label(self.tab, text="Destinatario:").grid(row=2, column=0, sticky="e", padx=5, pady=5) - self.entry_destinatario = tk.Entry(self.tab) - self.entry_destinatario.grid(row=2, column=1, padx=5, pady=5) - - tk.Label(self.tab, text="Asunto:").grid(row=3, column=0, sticky="e", padx=5, pady=5) - self.entry_asunto = tk.Entry(self.tab) - self.entry_asunto.grid(row=3, column=1, padx=5, pady=5) - - tk.Label(self.tab, text="Mensaje:").grid(row=4, column=0, sticky="ne", padx=5, pady=5) - self.text_mensaje = tk.Text(self.tab, height=5, width=40) - self.text_mensaje.grid(row=4, column=1, padx=5, pady=5) - - # Botón para enviar correo - tk.Button( - self.tab, - text="Enviar Correo", - command=self.enviar_correo - ).grid(row=5, column=1, pady=10) - - # Área de texto para mostrar correos recibidos - self.text_correos = tk.Text(self.tab, height=10, width=60) - self.text_correos.grid(row=6, column=0, columnspan=2, padx=5, pady=5) - - # Botón para recibir correos - tk.Button( - self.tab, - text="Recibir Correos", - command=self.recibir_correos - ).grid(row=7, column=1, pady=10) - - def enviar_correo(self): - self.email_client.enviar_correo( - self.entry_email.get(), - self.entry_password.get(), - self.entry_destinatario.get(), - self.entry_asunto.get(), - self.text_mensaje.get("1.0", tk.END).strip() - ) - - def recibir_correos(self): - self.email_client.recibir_correos( - self.entry_email.get(), - self.entry_password.get(), - self.text_correos - ) diff --git a/app/game.py b/app/game.py index 0ab6962..98f2386 100644 --- a/app/game.py +++ b/app/game.py @@ -1,8 +1,6 @@ -import pygame import threading import random import time -import tkinter as tk from tkinter import Frame, Canvas, messagebox, Button class HiloJuego: diff --git a/app/gestor_tareas.py b/app/gestor_tareas.py index 1a361e1..89a0697 100644 --- a/app/gestor_tareas.py +++ b/app/gestor_tareas.py @@ -8,12 +8,10 @@ class GestorTareas: def __init__(self, frame): self.frame = frame - # Configurar estilo para ttk.Frame style = ttk.Style() style.configure("Custom.TFrame", background="white") self.frame.configure(style="Custom.TFrame") - # Título self.title_label = ttk.Label( self.frame, text="Administrador de Tareas", font=("Helvetica", 16, "bold"), background="white" ) diff --git a/app/graphics.py b/app/graphics.py index f49e994..cf51a97 100644 --- a/app/graphics.py +++ b/app/graphics.py @@ -1,3 +1,4 @@ +from tkinter import messagebox import requests import yfinance as yf import threading @@ -33,7 +34,7 @@ def actualizar_grafico_criptomonedas_api(canvas, ax): ax.legend() canvas.draw() except Exception as e: - print(f"Error al obtener datos de criptomonedas: {e}") + messagebox.showerror("ERROR", f"Error al obtener datos de criptomonedas: {e}") time.sleep(60) @@ -57,7 +58,7 @@ def actualizar_grafico_ibex_api(canvas, ax): ax.legend() canvas.draw() except Exception as e: - print(f"Error al obtener datos del IBEX: {e}") + messagebox.showerror("ERROR", f"Error al obtener datos de IBEX: {e}") time.sleep(60) diff --git a/app/monitorization.py b/app/monitorization.py index 4231ef3..04fafc2 100644 --- a/app/monitorization.py +++ b/app/monitorization.py @@ -1,7 +1,5 @@ from tkinter import messagebox -import threading import time -import datetime import psutil def monitor_cpu_usage(label): diff --git a/app/panel_derecho/__pycache__/chat.cpython-313.pyc b/app/panel_derecho/__pycache__/chat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc258ff5ef82a63f47f72371ddfa840fc9248349 GIT binary patch literal 3796 zcma)9OKcm*8J>OdA+0FuVTn>Cdi8J|Iu8Am-O9Ec$$He5MJHUTwIQP0^>Rh8L+z@w zE5~60!zkK9DO5N>ZKQ`D(q3X0MdKoe`p`qITzV?Vm{lZ;b#!=h@Ma#p=!+)!KrxAvsb<{{|cMQKFz zVp^myBZfq}8(pI?OEhCnWT6g-9MnNEfJ50}JZ!UPugcb!94uCtI=w_K>2FR#br)UQ zm8(u(5^|K^5z!pV`6j^epjXv$kj(j4fU+n~Cnw{a9WYgVUBx!LWMIo~QgWKATasxg z@2Hj?Rt#NN6-&+7t&6H|%GXp=!m6TO(K3b^XKdC~a|;+Q8sRWhCfQ981p8fOI6ViN z&Ge#yWj#G^D9fP0GSf$-@zI&I2%Bn(W|)Gi3s}vh-+IrwYUt^i<(#Fxg$+eD4KqEd z>2eNw*T95PJ%d$qYQ|9~FD<2)WL?ck85PXAYNQpow#3qVc1V&mU9%*~ZX!3C@VfzG z-2ha#P^F`1wXxD3T@6=y`&QUesJqe?TVYotmHxpM_DN{ZCWs-Wda2MMtDWm#h0(hx zhUSTgQ&N7T?%Q8KUww|6K;7!eudxF+l5uVcYr18(sHUOJ<8#`}#AX8&~g zkCzMk&Xo8!346v@lk0p-q4h|KKk6*#FY^6Q_`&Dv&CyLn!xnr;nHN6ig^xN52WAU% z-!063udx48iN9Rw?Oz$coqRv}a)SfG7bXG6kH;FvB4{nraF$`#qJ3i{%-RSCZKa+? zRW|lJYy?!o%I)PfZy?i&(0p}uY6rdn0x3YbCnEs6LE|@N8}zq5IFRV9Z+?}02fZ49(O|(XI!8MJu`6+4Ej;U1%1Tb#ixzk=D>aHU>8|sLUjxM zv$^f|&D%HYak&3{iJ#t}+3*Zi2l9a;Kk(7i$I0I$ADnnRc(TNwa*#bxMKcBR^=e+9 z`YZJpvk;#y^B0Qzg_j!)k$7PO;Lz`eMg{apXc&tykM?(ug_*T52kk9Eg#>vgs9t_6DB?`NP3!a=ovOM|Q16vGSIxx(`8Ot9ALKt5 ziN+R$YfCl3ftuh$;VFH;l2Cjt=n6Lrk03h zzwQp;>@z<>ka}ei#L0Tr?{QdP^AG`#-Bk;=?gU&1F9BTlzV%#BY6yHB7U1~#R-dYV z$~G)4lZlDis|pGPn9n{HaWRtxD^*UrOK}w~f>N(_itsNgGK1^)TO)cG(SbsTos2Dp`jtv!KLk}{KV<$hD`bTH1 za_~3_d=Ima4^9@M2P)B?a`a#^daxWlUW^_uoR}^}FRVv<3&N4Y(W%0b?-XL^o4ixR%L`+NfdV5!)=fp$mfm!H2S!zF3 z*}MOP@jFQvez_5Z`4?skjJ-43GuDM39d3nst*hZI&8!XV9y`sfo#vpO7tZOBC(Anc zVlQDJsf~A$W+IW8jnj4jvY#xZ>w%1{E*g3=&N`QGhpnqvl{1>2wF9$G)8@dzW6N{1 z_y8CHtDPyw%l^PiEimz*cE`>)Pae}uKm`~5726!{e%6n|kwWB9DRg+frMukH_jya- zz0Qa2#g>tkKqVBp&A-q8B7#Xy8Rs1T@5TfI7w3P2lOrYLbUu9e$ce-u_#M_bpac9N zf`nd|7gb2;8zgDb$Smhbo0p^?F3UM@rdg5}G;CTqO;>e8l5jIo+)JuKQaOj&ElF^8 zOH(A-vaoh#8N8e%-A9gD4&*R#L{3E-jvxOSdPZ~H-lvfUZs2J&#LZA&weRLS-8DoC z&O;JjO#bfKLF*kz?SN3kt-IQWNsAoZ1$F3@dxQ~u3dRV%owNr+2St5>n!Z50{*Ge* OVUJRsPZ3nk%KrjCY9~_w literal 0 HcmV?d00001 diff --git a/app/panel_derecho/__pycache__/panel_derecho.cpython-313.pyc b/app/panel_derecho/__pycache__/panel_derecho.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82680cd64a679482a08d8085895b4df3013ca8fd GIT binary patch literal 13191 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 literal 0 HcmV?d00001 diff --git a/app/chat.py b/app/panel_derecho/chat.py similarity index 84% rename from app/chat.py rename to app/panel_derecho/chat.py index b9f905c..99e9c38 100644 --- a/app/chat.py +++ b/app/panel_derecho/chat.py @@ -1,8 +1,9 @@ import socket import threading +from tkinter import messagebox class Chat: - def __init__(self, server='127.0.0.1', port=3333): + def __init__(self, server='127.0.0.1' , port=3333): #192.168.120.106 self.server = server self.port = port self.client_socket = None @@ -18,18 +19,18 @@ class Chat: print("Conectado al servidor de chat.") self.escuchar_mensajes() except Exception as e: - print(f"Error, no se pudo conectar al servidor: {e}") + messagebox.showerror("ERROR", f"Error, no se pudo conectar al servidor: {e}") self.connected = False def enviar_mensaje(self, mensaje): """Enviar un mensaje al servidor.""" if not self.connected: - raise ConnectionError("No estás conectado al servidor.") + messagebox.showerror("ERROR", "No estas conectado al servidor") try: self.client_socket.send(mensaje.encode('utf-8')) print("Mensaje Enviado") except Exception as e: - raise ConnectionError(f"Error al enviar el mensaje: {e}") + messagebox.showerror("ERROR",f"Error al enviar el mensaje: {e}") def escuchar_mensajes(self): """Escucha mensajes entrantes desde el servidor.""" diff --git a/app/panel_derecho.py b/app/panel_derecho/panel_derecho.py similarity index 62% rename from app/panel_derecho.py rename to app/panel_derecho/panel_derecho.py index 92a076b..c5920bd 100644 --- a/app/panel_derecho.py +++ b/app/panel_derecho/panel_derecho.py @@ -1,12 +1,14 @@ import tkinter as tk from tkinter import messagebox -from app.chat import Chat +from app.panel_derecho.chat import Chat import threading import time import pygame # Para reproducir música class PanelDerecho: def __init__(self, frame): + self.usuario_email = None + self.usuario_password = None # Inicializar el cliente de chat self.chat_client = Chat() try: @@ -22,13 +24,28 @@ class PanelDerecho: self.frame.configure(bg="lightblue", width=200) self.frame.grid_propagate(False) self.frame.columnconfigure(0, weight=1) + + # Botón de iniciar sesión (y cerrar sesión) + frame_login = tk.Frame(frame, bg="lightblue") + frame_login.grid(row=0, column=0, pady=5, sticky="ew") - # Título del chat - chat_label = tk.Label(self.frame, text="Chat", font=("Helvetica", 12, "bold"), bg="white", fg="red") - chat_label.grid(row=0, column=0, sticky="ew", pady=5, padx=5) + self.btn_login = tk.Button( + frame_login, text="🔑 Iniciar Sesión", + font=("Helvetica", 11, "bold"), bg="orange", fg="white", + command=self.mostrar_login + ) + self.btn_login.pack(padx=5) + + self.btn_logout = tk.Button( + frame_login, text="🚪 Cerrar Sesión", + font=("Helvetica", 11, "bold"), bg="red", fg="white", + command=self.cerrar_sesion + ) + self.btn_logout.pack(padx=5) + self.btn_logout.pack_forget() # Ocultar hasta iniciar sesión # Área de entrada de mensaje - mensaje_label = tk.Label(self.frame, text="Mensaje", font=("Helvetica", 10), bg="lightblue") + mensaje_label = tk.Label(self.frame, text="CHAT", font=("Helvetica", 10), bg="lightblue") mensaje_label.grid(row=1, column=0, sticky="w", padx=5) self.entrada_mensaje = tk.Text(self.frame, height=5, bg="lightyellow", wrap="word") self.entrada_mensaje.grid(row=2, column=0, sticky="ew", padx=5, pady=5) @@ -62,6 +79,62 @@ class PanelDerecho: # Iniciar actualización de mensajes self.iniciar_actualizacion_mensajes() + + def mostrar_login(self): + login_window = tk.Toplevel() + login_window.title("Iniciar Sesión") + login_window.geometry("300x200") + + tk.Label(login_window, text="Correo Electrónico:", font=("Helvetica", 11)).pack(pady=5) + entry_email = tk.Entry(login_window, width=30) + entry_email.pack(pady=5) + + tk.Label(login_window, text="Contraseña:", font=("Helvetica", 11)).pack(pady=5) + entry_password = tk.Entry(login_window, width=30, show="*") + entry_password.pack(pady=5) + + def validar_login(): + email = entry_email.get() + password = entry_password.get() + + if not email or not password: + messagebox.showerror("Error", "⚠️ Debes ingresar ambos campos.") + return + + self.set_credentials(email, password) + messagebox.showinfo("Éxito", "✅ Inicio de sesión exitoso.") + self.btn_login.pack_forget() + self.btn_logout.pack() + login_window.destroy() + + tk.Button( + login_window, text="🔑 Iniciar Sesión", + font=("Helvetica", 11, "bold"), + bg="orange", fg="white", + command=validar_login + ).pack(pady=10) + + def cerrar_sesion(self): + """Cierra sesión y elimina las credenciales almacenadas.""" + self.clear_credentials() + messagebox.showinfo("Sesión Cerrada", "🚪 Has cerrado sesión correctamente.") + self.btn_logout.pack_forget() + self.btn_login.pack() + + def set_credentials(self, email, password): + """Establece las credenciales de inicio de sesión.""" + self.usuario_email = email + self.usuario_password = password + + def clear_credentials(self): + """Limpia las credenciales de inicio de sesión.""" + self.usuario_email = None + self.usuario_password = None + + def get_credentials(self): + """Devuelve las credenciales de usuario (email, password).""" + return self.usuario_email, self.usuario_password + def enviar_mensaje(self): """Enviar un mensaje al servidor.""" @@ -126,4 +199,4 @@ class PanelDerecho: """Reinicia la música desde el principio.""" pygame.mixer.music.stop() pygame.mixer.music.play() - messagebox.showinfo("Reproducción", "Reproduciendo desde el principio.") + messagebox.showinfo("Reproducción", "Reproduciendo desde el principio.") \ No newline at end of file diff --git a/app/panel_izquierdo/__pycache__/panel_izquierdo.cpython-313.pyc b/app/panel_izquierdo/__pycache__/panel_izquierdo.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..914b7143c20943978c7e4e64439a5037b0b122b9 GIT binary patch literal 7367 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 af1~z4poTx7rav)PDQb}V23eKG=Klahylj5} literal 0 HcmV?d00001 diff --git a/app/panel_izquierdo.py b/app/panel_izquierdo/panel_izquierdo.py similarity index 93% rename from app/panel_izquierdo.py rename to app/panel_izquierdo/panel_izquierdo.py index 4f18d40..15fa9a9 100644 --- a/app/panel_izquierdo.py +++ b/app/panel_izquierdo/panel_izquierdo.py @@ -26,18 +26,22 @@ class PanelIzquierdo: self.weather_label.pack(pady=5) #self.update_weather() Comentado para que no gaste limite de requests en la api - # Sección: Scrapping + # Sección: Scraping scraping_frame = tk.Frame(self.frame, bg="white", bd=2, relief="sunken") scraping_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) scraping_title = tk.Label(scraping_frame, text="Scraping", bg="white", font=("Helvetica", 12, "bold"), fg="navy") scraping_title.pack(pady=5) + self.entry_url = tk.Entry(scraping_frame, font=("Helvetica", 10), width=30) + self.entry_url.pack(pady=5) + self.entry_url.insert(0, "https://www.amazon.es/") # URL por defecto + boton_scrapping = tk.Button( scraping_frame, - text="Iniciar Scrapping", + text="Iniciar Scraping", command=lambda: threading.Thread( target=scraping.iniciar_scraping_y_insercion, - args=("https://www.amazon.es/", text_widget) + args=(self.entry_url.get(), text_widget) ).start(), bg="lightgreen" ) @@ -55,7 +59,6 @@ class PanelIzquierdo: self.news_label.pack(pady=5) #self.update_news() Comentado para que no gaste limite de requests en la api - # Configuración para que las filas crezcan dinámicamente self.frame.rowconfigure(2, weight=1) def update_weather(self): diff --git a/app/scraping.py b/app/scraping.py index f01fa0c..385c96b 100644 --- a/app/scraping.py +++ b/app/scraping.py @@ -1,3 +1,4 @@ +from tkinter import messagebox import requests from bs4 import BeautifulSoup import threading @@ -31,9 +32,9 @@ def extraer_enlaces(url, cola, text_widget): cola.put(enlace_str) if text_widget: text_widget.insert('end', enlace_str + "\n") - print("Extracción de enlaces completada.") + messagebox.showinfo("Scraping", "Extracción de enlaces completada.") else: - print(f"Error al acceder a la URL: {respuesta.status_code}") + messagebox.showerror("ERROR", f"Error al acceder a la URL: {respuesta.status_code}") except requests.exceptions.RequestException as e: print(f"Error durante la solicitud HTTP: {e}") @@ -67,7 +68,6 @@ def insertar_enlaces_mysql(cola): print("Inserción en la base de datos finalizada.") -# Función para iniciar el scraping y la inserción en hilos def iniciar_scraping_y_insercion(url, text_widget): cola_enlaces = Queue() # Configurar los hilos como daemon diff --git a/app/todo_list.py b/app/todo_list.py index 30a54d1..89836c1 100644 --- a/app/todo_list.py +++ b/app/todo_list.py @@ -1,12 +1,69 @@ import tkinter as tk +from tkinter import ttk import threading -import time + +def crear_solapa_todo(tab): + """Crea una pestaña de To-Do List que usa TODO el espacio disponible.""" + + tareas = [] + + # Configurar el frame principal para que use todo el espacio + frame_principal = ttk.Frame(tab, padding=10) + frame_principal.grid(row=0, column=0, sticky="nsew") + + tab.columnconfigure(0, weight=1) + tab.rowconfigure(0, weight=1) + frame_principal.columnconfigure(0, weight=1) + frame_principal.rowconfigure(1, weight=1) # La lista de tareas ocupa el mayor espacio + + # Estilos para botones de colores + style = ttk.Style() + style.configure("BotonAzul.TButton", font=("Arial", 12, "bold"), foreground="#007BFF", background="#007BFF") + style.configure("BotonRojo.TButton", font=("Arial", 12, "bold"), foreground="#DC3545", background="#DC3545") + style.configure("BotonVerde.TButton", font=("Arial", 12, "bold"), foreground="#28A745", background="#28A745") + + # Entrada para agregar nuevas tareas + entry = ttk.Entry(frame_principal, font=("Arial", 14)) + entry.grid(row=0, column=0, padx=5, pady=5, sticky="ew") + + # Marco para la lista con scroll + frame_lista = ttk.Frame(frame_principal) + frame_lista.grid(row=1, column=0, pady=5, sticky="nsew") + + frame_lista.columnconfigure(0, weight=1) + frame_lista.rowconfigure(0, weight=1) + + # ListBox con scrollbar + todo_listbox = tk.Listbox(frame_lista, font=("Arial", 14), selectbackground="#00A8E8") + todo_listbox.grid(row=0, column=0, sticky="nsew") + + scrollbar = ttk.Scrollbar(frame_lista, orient="vertical", command=todo_listbox.yview) + scrollbar.grid(row=0, column=1, sticky="ns") + todo_listbox.config(yscrollcommand=scrollbar.set) + + # Marco inferior para botones + frame_botones = ttk.Frame(frame_principal) + frame_botones.grid(row=2, column=0, pady=5, sticky="ew") + frame_botones.columnconfigure((0,1,2), weight=1) + + # Botones + boton_agregar = ttk.Button(frame_botones, text="➕ Agregar", style="BotonAzul.TButton", command=lambda: agregar_tarea(entry, todo_listbox, tareas)) + boton_agregar.grid(row=0, column=0, padx=5, sticky="ew") + + boton_eliminar = ttk.Button(frame_botones, text="🗑️ Eliminar", style="BotonRojo.TButton", command=lambda: eliminar_tarea(todo_listbox, tareas)) + boton_eliminar.grid(row=0, column=1, padx=5, sticky="ew") + + boton_marcar = ttk.Button(frame_botones, text="✔️ Marcar", style="BotonVerde.TButton", command=lambda: marcar_tarea(todo_listbox, tareas)) + boton_marcar.grid(row=0, column=2, padx=5, sticky="ew") + + # Inicializar la lista vacía + actualizar_todo_list(todo_listbox, tareas) def actualizar_todo_list(todo_listbox, tareas): - """Actualizar el contenido del ListBox con las tareas actuales.""" + """Actualizar la lista de tareas en la interfaz.""" todo_listbox.delete(0, tk.END) for tarea, completada in tareas: - estado = "[X]" if completada else "[ ]" + estado = "✅" if completada else "⬜" todo_listbox.insert(tk.END, f"{estado} {tarea}") def agregar_tarea(entry, todo_listbox, tareas): @@ -14,13 +71,13 @@ def agregar_tarea(entry, todo_listbox, tareas): def tarea_hilo(): nueva_tarea = entry.get() if nueva_tarea.strip(): - tareas.append((nueva_tarea, False)) # Agregar tarea como no completada + tareas.append((nueva_tarea, False)) # Agregar como tarea no completada todo_listbox.after(0, actualizar_todo_list, todo_listbox, tareas) entry.delete(0, tk.END) threading.Thread(target=tarea_hilo).start() def eliminar_tarea(todo_listbox, tareas): - """Eliminar la tarea seleccionada de la lista en un hilo separado.""" + """Eliminar una tarea de la lista.""" def tarea_hilo(): seleccion = todo_listbox.curselection() if seleccion: @@ -30,39 +87,12 @@ def eliminar_tarea(todo_listbox, tareas): threading.Thread(target=tarea_hilo).start() def marcar_tarea(todo_listbox, tareas): - """Marcar la tarea seleccionada como completada o no completada en un hilo separado.""" + """Marcar la tarea seleccionada como completada o no.""" def tarea_hilo(): seleccion = todo_listbox.curselection() if seleccion: indice = seleccion[0] tarea, completada = tareas[indice] - tareas[indice] = (tarea, not completada) # Alternar el estado de completada + tareas[indice] = (tarea, not completada) # Alternar estado todo_listbox.after(0, actualizar_todo_list, todo_listbox, tareas) threading.Thread(target=tarea_hilo).start() - -def crear_solapa_todo(tab): - """Función para inicializar la funcionalidad de la lista To-Do en la solapa.""" - tareas = [] - - # Entrada para agregar nuevas tareas - entry = tk.Entry(tab, font=("Arial", 12)) - entry.pack(pady=10, padx=10, fill="x") - - # Botón para agregar tareas - boton_agregar = tk.Button(tab, text="Agregar", command=lambda: agregar_tarea(entry, todo_listbox, tareas)) - boton_agregar.pack(pady=5) - - # ListBox para mostrar las tareas - todo_listbox = tk.Listbox(tab, font=("Arial", 12), height=10) - todo_listbox.pack(pady=10, padx=10, fill="both", expand=True) - - # Botón para eliminar tareas - boton_eliminar = tk.Button(tab, text="Eliminar", command=lambda: eliminar_tarea(todo_listbox, tareas)) - boton_eliminar.pack(pady=5) - - # Botón para marcar tareas como completadas o no completadas - boton_marcar = tk.Button(tab, text="Marcar como hecho/no hecho", command=lambda: marcar_tarea(todo_listbox, tareas)) - boton_marcar.pack(pady=5) - - # Inicializar la lista vacía - actualizar_todo_list(todo_listbox, tareas) diff --git a/main.py b/main.py index 7d1c09b..7615d77 100644 --- a/main.py +++ b/main.py @@ -1,24 +1,17 @@ -import tkinter as tk -from tkinter import Menu # Importar el widget Menu -from tkinter import ttk # Importar el widget ttk -from tkinter import messagebox -from app import monitorization -from app import panel_derecho -from app import panel_izquierdo -from app import pomodoro +import tkinter as tk +from tkinter import Menu, ttk, messagebox + +from app import monitorization, pomodoro, game, scraping, gestor_tareas, graphics, todo_list +from app.panel_derecho import panel_derecho +from app.panel_izquierdo import panel_izquierdo +from app.email.emailTab import EmailTab +from app.email.reciveEmailTab import ReciveEmailTab + import threading import time import datetime -import psutil -from app import scraping -from app import game -from app import gestor_tareas from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt -import random -from app import graphics -from app import todo_list -from app.emailTab import EmailTab def update_time(status_bar): while True: @@ -114,35 +107,6 @@ style.configure("CustomNotebook.TNotebook.Tab", font=("Arial", 12, "bold")) notebook = ttk.Notebook(frame_superior, style="CustomNotebook.TNotebook") notebook.pack(fill="both", expand=True) -# Crear cinco solapas en el frame superior -for i in range(1, 7): - tab = ttk.Frame(notebook) - if i == 1: - notebook.add(tab, text="Scrapping", padding=4) - text_scrapping = tk.Text(tab, wrap="word") - text_scrapping.pack(fill="both", expand=True) - elif i == 2: # Identificar la solapa 2 - notebook.add(tab, text="Pomodoro", padding=4) - pomodoro.PomodoroTimer(tab) - elif i == 3: - notebook.add(tab, text="Gestor de tareas", padding=4) - gestor_tareas.GestorTareas(tab) - elif i == 4: - notebook.add(tab, text='Juego', padding=4) - game_frame = tk.Frame(tab) - game_frame.pack(fill="both", expand=True) - hilo_juego = game.HiloJuego(game_frame) - elif i == 5: - notebook.add(tab, text='To Do List', padding=4) - todo_list.crear_solapa_todo(tab) - elif i == 6: - EmailTab(notebook) - - else: - notebook.add(tab, text=f"Solapa {i}", padding=4) - # Añadir un Label en cada solapa para diferenciarla - label = ttk.Label(tab, text=f"Contenido de la Solapa {i}") - label.pack(pady=10) # Notebook para las pestañas en el frame inferior notebook_inferior = ttk.Notebook(frame_inferior, style="CustomNotebook.TNotebook") @@ -200,11 +164,45 @@ thread_network_used_monitor = threading.Thread(target=monitorization.monitor_net thread_network_used_monitor.daemon = True thread_network_used_monitor.start() -# Lados verticales +#Iniciar panel derecho panel_d = panel_derecho.PanelDerecho(frame_derecho) -panel_i = panel_izquierdo.PanelIzquierdo(frame_izquierdo, text_scrapping) + +# Crear cinco solapas en el frame superior +for i in range(1, 8): + tab = ttk.Frame(notebook) + if i == 1: + notebook.add(tab, text="👀 Scrapping", padding=4) + text_scrapping = tk.Text(tab, wrap="word") + text_scrapping.pack(fill="both", expand=True) + elif i == 2: # Identificar la solapa 2 + notebook.add(tab, text="🍅 Pomodoro", padding=4) + pomodoro.PomodoroTimer(tab) + elif i == 3: + notebook.add(tab, text="📇 Gestor de tareas", padding=4) + gestor_tareas.GestorTareas(tab) + elif i == 4: + notebook.add(tab, text='🎮 Juego', padding=4) + game_frame = tk.Frame(tab) + game_frame.pack(fill="both", expand=True) + hilo_juego = game.HiloJuego(game_frame) + elif i == 5: + notebook.add(tab, text='📝 To Do List', padding=4) + todo_list.crear_solapa_todo(tab) + elif i == 6: + EmailTab(notebook, panel_d) + elif i == 7: + ReciveEmailTab(notebook, panel_d) + + else: + notebook.add(tab, text=f"Solapa {i}", padding=4) + # Añadir un Label en cada solapa para diferenciarla + label = ttk.Label(tab, text=f"Contenido de la Solapa {i}") + label.pack(pady=10) + +# Panel izquierdo +panel_i = panel_izquierdo.PanelIzquierdo(frame_izquierdo, text_scrapping) root.protocol("WM_DELETE_WINDOW", detener_aplicacion) # Ejecución de la aplicación -root.mainloop() +root.mainloop() \ No newline at end of file