From 7c9df6e44b0732414db314a61148c25a17222605 Mon Sep 17 00:00:00 2001 From: Qing <44231502+byemaxx@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:49:06 -0400 Subject: [PATCH 1/4] New: added an option in Settings to enable user to set the color of the theme (white or dark) of the HTML plot. Updated the MetaX_Cookbook.md to include the new feature. --- Docs/ChangeLog.md | 6 + Docs/MetaX_Cookbook.assets/settings_page2.png | Bin 25962 -> 27614 bytes Docs/MetaX_Cookbook.md | 3 +- utils/GUI.py | 45 +- utils/MetaX_GUI/Setting.ui | 571 ++++++++++-------- utils/MetaX_GUI/Settings.py | 10 +- utils/MetaX_GUI/Ui_Setting.py | 401 ++++++------ utils/version.py | 2 +- 8 files changed, 590 insertions(+), 448 deletions(-) diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md index 4bef56c..90e020b 100644 --- a/Docs/ChangeLog.md +++ b/Docs/ChangeLog.md @@ -1,3 +1,9 @@ +# Version: 1.107.6 +## Date: 2024-06-19 +### Changes: +- New: added an option in Settings to enable user to set the color of the theme (white or dark) of the HTML plot. + + # Version: 1.107.5 ## Date: 2024-06-18 ### Changes: diff --git a/Docs/MetaX_Cookbook.assets/settings_page2.png b/Docs/MetaX_Cookbook.assets/settings_page2.png index 2564529dd910f978cd2a5fbbeaba732ed94d3660..e081c1897dc32f8a536bc46243ec55bd0e444497 100644 GIT binary patch literal 27614 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 z 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 literal 25962 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 D?IJwYUG$z$8lsYR))=^?HxkxLd=|(Uw zRvHF##?V&c;o)#>1uf(?FRK0$J%Qt%)c$N9f8UZUr#qL|v!FOsn1Yy5F;HyYXi2lA za{a87`9x$&=LLra(Wc*riP7^pgMen0lJQE37Cr|n)x{jXVZDe{L#vNxs@GSuYRcE7g(;nO#$1^Z@c2ejqy=-gP6dPBHycwQ4U#q*#SWs z4jNmfv}OAH3{MsKx#BI8 z-bHGxNL3QuJzr-@=eXHICKVj$+(A%$B`3F*Gj|FJX)uhS}3?o6~)zB9=-@cg==YE}QB8%6` zC;n*)!-cBMPgRB5%OgU5?FS+d3_@6Y&5eZnc;Or2`xrU$ZTq)0^M99=&#{O*RbIc@ z)6K&Jlq$blh5Xpv(eN%q3oEPndWZZj!?8M30qjmkKCh!y@tm~B;?QBkavhgNr@76H zhF5LC!*6D3fox$M%z)}@C^-I^qlbrfxaAWduhlgv-?@P*!}a#j@F)j|!u}z>D{zUx zii@rIH;vKL?@$RrHA}meUhg}B1wk4b;Ut%nV}3X9%lJ9ZMx$Yb<~vlG-tjK!Nmq+?FZNW~9y@08dct56yC zMBYm5A$5-tI{iZ+F3o!L?jI964Xa)vF!7hxBmGQBhJe}P3Q;nl44cxKZqElS!Ot9{bjR@ zi(dDAW&BQ=b4+lM>nZrcIo;>r$`j2D`|*iCO^Zb}7>{;}65c&&I=pp&HNt<1-WDAH zK~#W9wr(t56(V6w#eBmDMZ4DA41>^@>}cQr9jW~U+n3ELCkld&%h9t!D3a3{f`@-H zZ%FSp6SH%Xi`%s{34mrt1NQDzY?>|BXGIYe_9*%IzHBx3g2@CRn(2?#60+JoWw_pp z;4z9x9g-4rJj}ju7CPNB+QY%Xjg8RVot1=ps1=R+!_q-M=E5J1F)9K{L-~DH=>9gs zcP{bWlf@a6+>_hqSzcl{Ve&$D94CbiXL!VSlT9N6kA?`KJ8He2FSpylUo$71Q|z(-K-J7o@Wy)z^M`fbQZ#&70qAlnOKp@232a2YZmu~Sv3`U#Rf=CG zP~%8!+^?h`W9@Mf`@WAxYr`39&YsZUbKheO7o_quEoi(MZPLf}5%6s533`9RC9`|^ z*mx=i>Uy;!*Xy?MrTM$j=w*I~7&*Wk>ATyv6C&rB0!ch4t!-Y^)%3&C&mS#+z#n>W zaieg**{|g+!vS&-c1$h~773?KYsml3adZ2RfBW02_^%XL3iu)7|9Nd#Tf6>zj8u}X5_U|&CSZmAD zL#BJo@uiZTe)f1FO8(@riU0l7X-fqjYjt`&vb8_pc$Oru{b&V&#HniHAk}dIttwl0 zEy?LBJ)%@6dq~}!=%EW6C>Hj!s#ILo3w2P`4fvyf|E59#$n-V+nVJ{xlV_I-blO&b zI$Q10QUT%+7^I7>IC%5ePIE%QJzV1QKbE{k`KJ%BrOV0;iuxEM!2_8DH30cqTB*NS zL-c?^wr=!Y9>QTVGN5~-Plu{47mZ{uiCwfDWb{MAe6Gm$T6T$ki+T>iy-bTgr6Q)k z!XTw9Rs6-|PHkuDcIFSo#Zro2D>X6-Ytm{K2P*W9GiiQy>i?&&toz)%^P;h*0aBx< zbM7;%@Yj&zd`}04PfEetdfdaZxMC-`B+B^2FHm)O*7d*60(b)D)D-U2SOd0CG|#!33-lYTnO5u9o=A_C zenh0oV89zX_GIF_-|&g>;)J_B^ew<5#Dccd5RG~e{mPmmF3%r=AHs#u@jM0p?jV+< z2*_JjEcxck5qf?4o1!28zLxarFN{E+O8k&sywI0Qxi)%;uKoGi|FM>W)V@vM0~1@8 z`#Ow=o#%5NGV#MiHGE-i=YlPS9{!N6>a9yDpLI^9;vH5DJm7&0DZmp^7q2aYfL=9X z%KbV=d6;7iN?5)bj9Y^|2`9ZkC$6VF8?;~N%o0G_^QirxAc6A%{dx{H@(i}!TK2P{ z_R$q(JO{k$ee3TIpVkzubDt{V)3Pi(t$KhuioGA!44M?yk^ACkLdZK1*wlG(iDjjxS)hk!3t2RWAsABc9)rRLZk@ERvdQDnzH4f60u1};+ z{2`IPPdTu4YFkw>BUD@E5y6G(!W2)Qn^V;}el2|aw%9lIMc%h(f1$0`)BiFz|20Sd zAEmzdSm{or`YR9!PuSYJ{_{~HY(-XTd@}kUftV=GbVS zmITV~lr%li!5@Pu5~avz;N@o=SdIi|9I86LU5ly|Th8%nt&-h~T|`g8^r6ov1mD9` z6(;jt%9tYvclwcqPdAfoB}{%@Z6<4d&MZ{ecW;hNV(UO1^Eb#_8uCrr_QBuH1x9&P zgu#)4jkSC8UOojScV~vMAkOI%Gr!`<3l5XFc8rzmAy&y^ldvScgr5$KJ(APm?~n;7 zY&I@?>y5^cL3T&8iS$ZrS(haai8@Vv)zwSeTi=`NNGbZuCSmqxG&5Toj_vZ;s$VS} zot>Lc$7TAAE6m+&ZQ+Y9%g`CMi^W4LKfAcZE{cLe6M)OUski)2Xr}RDNNi6n@n?HhJdGDfeyoQn_C1k))^j2w z6JACa)9u&x=|C#L*molqEk~?gaj=$pdGj{w2qJdEVnLS`7>J66854qy34^JATM zThL*m2O+#8^Rhr7n^Q!lkQ1|82l`ofKLLIpkyHd|I zo^%N_=r(cS%FGQ)b~_9aRrK5?S{?pMuQW@Nr1x^xk6Wy)Vl^;>Kn&ck>DrCL24(hP!>lq}+})<8=3 zKKWLo3^cqZ>T*lduu}4(58DhN&YXHC^r;$~Of!@>puGwUTQ9cgD+ID_1{E_lK|0?3 z7Q_?qeIQ~=Yt*0S+Y;s)Du!`*T<6igbSIc=Bxvx+WWNSe(*6lesTNyAe;M{t$R9U;FjR&G+*AZWdY+J$en{SL!%Pl;28~=;hUG>-2Z8+e$UQ)()PJYPR2^5(HIz%$6B@E|E}Q&HS$Bv463o*c<7& z?DeY~yO}vE&OuTJc{8)KZWoI#LU*SVV(X54AP{JGG3y1WI%L?oxZ2objzJ)w3a*_< zy3%G+L)C)?ZcC&aL+(wv{Pc7a6f(fRkI5f7g!2+Mgr-4{bsF&T^~OtJ*84lnieUK5 zQ)B0USbu}-M7m?;`{K`QLL!g>%$}XzIGly{OqD+OiJCNXgi5I%U9_Dk-bQu+7O9DS z{)qJ^{wzspbypW>d`H@y13PzgNXW==cP_K7_!(z$!78-OtFC$rl5 zi@jNglOb+0KF5M3>&yV4?IYIXCyj~5BZY&$JOtq;Y1;3B2cb-6%_r=FyTr&+Lr;cc zfb~7=v@xO+g#K)>&DhM&%~Vc!)?1Q+n;fF>%&)$z;ioCS3GMC2$_i)gL3sG=UT$rc zGUpf1Z?qJGSemgqNQ3=+U)sSuBgI>nTAGbaczm(E%|UfPRRmjui{jC#s*B^Ns|o3U zRJHpP0h-Wcry$!taQuWPb$E?@$1X_kOC6-*iZ;^A(4xV`t8?3tFO_{jAZ#^1De8>1 z19di+z1*S6YX|UH=C-yiur!Y_Qh~XpB|UhJF~?|OszB}4ptKE~o}?vAZYRYsfL4-y zz|=5?_X_eRZ7_~lGjE%E8=iqFZ8E$-8sDDCIRDj`KH>14n%V=o>zh4|R)Um^22nBN zO1FT`60vEvbfX2h6V5C#sfTk-N-T+e=bcS$2Lb6onJvj{`|r0LDy|p(($m6XxXTF` zS70%&K=}70klv_HTb=sKn2Iq8&q|O7jB;CDPVR}~f_0&G3BaF2*VzQ4m(7uX-o2=7!~DxLIfSS(9wGB5Al&lbOK%lE zB7{;U%1$>TBRsch+^=!x&TM(x9TZ>&9e^v52^V7 zl*0f2^*N`>#wQ3F)3J=|5 zfK>lHkht}Be7kB?a;A~o$*5ezdGGfa-z-8{-J2?{$u}5wdM-|{>>p=>FMyP5T*&~? z{SQ!njr3@V>93_mz8AdMioP#=1(UQs(^gF;bFve^)HS$B5@`wpz%fu@^Ir;{`~#WI zk^d#@{C885GaCeiNu(aTPFC5gZ%3Li`;qbXpTSw5B#t!A2C-5fF{zpPdxy96mwSVg zb2L~==~nLc?ZcRM09M844w-hAlce3d5~`0~qYU9l3PK9ve7_N*-={`dOPnDQWhYP7 zd3`*$X}WA~$Bm!vBoCGH&zNMW$RtV^U`~;Vaa-p3N_1~VIv4@b@d zxH$EV9t1sP`o{=gya)fP6?^Ub$~vh@+R~QOinNU+y<=m2%;lY4rob2Dn0E3X@Duh@ z({v1_ZEM%cIVrS4x@h-S>C`oHIUIq`fUTM)WKf+Yc%mB|T(qJF$T5)VCiN+{&U=Ch z=J*NEtUW2-w-8F_LI9_>9K<%wAz&(i2P16o0JbM?E-otl9XPz@{(9G{M1Pr%VwJM( zO7Wezn6ZPa{EtLfySDtfdrG4)YD+_yNrl8CJnK3;A)ZBLsafbh)>@7PV2i7Hq)usb zbxslV{OqI7$wnM^QB>Uo>!yme!kRUgY6_~z0^>!50IBvaQL12tplZa|{!)rJ4FE!u zn>qcKN={+wYVYV}?AB#OX1X3{b>W?ifUQ^{k$5J-Su;a_*TeKyog8BLwI z`q;wq=VPmvdn)#h7Ez`uR2d6bR7&>U^$E!M20!$@-9#N;|IU9qEYsJ+o2o}U6ZzW? z79*CA7Y;om(X1fLYZg~}(ccH9AZb|OJ$i+k*BH+=Vkh=v!M^ESM6^9HqP6q##nlrm!g6LI{GFKN2wUuIhxX=HC<(XN+g+*aIIxW=gSJoJC%7e<6)N~Mbr?C2YkU0n6KE;e92ca(EzJr`(_So$g zH_vIn$w=l+d*sXXjIL#h6XIdC^qA#O+bhU*aS=$S@YZK+9XOql^43&URdEPNTY=eu z+3=>X71M?AgF?Gx_eo^L%gqLgT=RFe;+<}S!}Siy$yU1pJ#krMTjS>Nq=Jc6wK#uQ z(}3{$>9~^t6ZObfx_S8uS>ne7BF~WN64WTRLMIg85~Cw|$_|prVq$?d9Xi!0`Hv#6 zFx})|X;6o3l;4G^mTNzp^b zZ*Dp}JAbAcyzUm{=bfvq_f7(9Eve*Sc!}hTke7K{#=UeGJ@!gkV1S%N@xX>@3vC_k zdDt-zuz+x+yVt}9=IZRhmyP&=QegHCu26befh+C8Aa$2}lZ0nytW+(;Vyw?F^19Gp zXk>C%IkRyEpYWHf8TNP*9b6r`2J?S?QvA8xlga3-@ap8RWg?1-Sz^&E-i$DEgVTOX z6I@^%J5MXsC@LxtMZ-odzmfgs!;|Lqboih&ZG&p3EtZ$cNG+?|f8WJ=Y?QVkQz~cD zQwJ$1b6onwO9mWq8c7rI0Agbl4F?Uof~T1ns#WeARgf$uW@=T=bVxq2sDpU90nXi9f5EU2-#y-j}J`iJCcMxt2nr0%LL!A1Dt)_ zRK(UfiHOwnqc6AeG7S1tr=EbWAXc+C z6;5(m;x0OSt5Bm6Hq{?C;D3Y!H z6^mG4Zc7r4poeE-SP-lOk3TJ_XZpHx+myemPO6G`RcDZSc5t{nx(!O4&P!YVq<$k5 zYL0Hc8nd3s^73VIY&CYIqfKAfbI(X{@rjL(^yQzNq@N-wGTzUlFVO=*=~O%9{YwTx zHeIWg!-a~T8X9lwgFUz2p9lGGiPaHP{Tw&Mu4!~H|c|Y8D@MZ|(d)fLXW|xsw42jPOu4A1!6MnpY?&mFg z9nbSil?vmess(%DrzkYEIZmDw+*~nkoaBF+@jUX zX9{AT6cvP(vzk_>rWWTXPlGQ?-L97(ONTM|C z#c8Ug&-OUA?yfCvqw_w4wnRJ~!?ky(=WFBm&1~VSyK!|@9~bnnIyX8AT?#T;L+r0g zqZaEQ==4WdGG3bze4`|)Mp@SV!GnZWxUIz*_zYJ#a`3F{(^lSdr)}!pr;-c&TC|U# zl{={68E?R!$56w61Q%a_Lo!ErU4UIR{D}^zqDuY|0^48%*}!eSn}D@PLh{Q0znM-c z!CxQE=r1?^9svr2<9|iNcurTzHE@gPlYba+pfcNeH;(Y=q4Esl#hV~Xv z&b=I)h?KeULri+YNTAVZe;~;s#p0<)m@p^xp0_=i{KANJ?q`-E7h~Iw*E>_iXEy$J zUpj{}rm}`zAtw#RMiG>NWR9?vUD)OPL8eKQz2oaBo^#oo$z8g)e+%;dO97DkIM8vv z|IGpZ*9-YO&K*m z)l73nS!hF)9#7#1KRB8p-;y8ylgxjW;u6$z(fXqEhSlBp34>D@d`dClJEUz@Ub#^dsp zxt;YqsBdh&cd|ru+u09PD+S256O@nox7`)LkIaFt&!#M!*+QWjMv75HG;@7l`u@vzw_6C3ky#!wQ{Q~0y0TI+dh1#B= z>96&N!qto?k#0T<2dIPPYJkpl>h5=4twI1W!1wj;(N@4Pjv;$^|0x{dY>MdP=|q*J zr46XpgiR7+$??u`5c2Ho?RjqJ#z*z7qBqsnUK}*SOPjpEdMkBGb~o8|6TF12wOXF@ zK+M7@#*)C7@e(P!*)pgdUDT?^G0uS@d4_i3i^G^eGbNfPHp)tlW((K1#;JyIb$bD9 zOG9$Q+)?{`&~6yoIo-=?O-oUsAeNn_K1?fhJE_7eSPv+IrADY(hw%&}sS4QCW+@?w z&0Ip~7;iobx0RrD*Hsz`;J&%AcgE-vEswbzPFHvayDP^vv7N1bkAZx9kN^l?*|&;H z2M0^0C$EOkekP(xO(vED_~0BTF)-~B!6z?&#GI$DVyn|yh+3FHOzz<3?nbeu#ppnO zexiWeX^k(vW9ASV#>C|IIsl-mh3k>ITi;m2+&xV!3=-%yco<)mO2oT!kbJBC8--9{ zeJz_mA&$y~AHp?HrwYam>Ue9%AePpez=Qs)I8wy9Rf|x`AC1nN6U(tI%5TMxjfR+X zVmA~g@=IhY=FksRCzuUSPOvS?NC3p#!LGADK{urdv0Op6F-#$pCkS#%^7F^P&H@12 zwiNE0sEwf2O0ZSwHk~M=KT(wSk{DHX7Dp@9+62Q?Xv{qEst)sTSKBNQcZ%2=1z_Kh ztf90Dm*6A+(8X4z1UEO%OwF$)q!>d$0_?FwH-W^SD5Qk)@8ta)R=)83$sG0kPP;-* z{clkbd2(CUwlIH0QX*c@m2b%|g6|LhjZy^c**&;^k1u8q+;_?OaMJeigN7wZ!hkM@ z^o;Z~hqIf61fc{>lsul&2Qh$7T<)c)B-eY&oR0ksRzz=Ggd;D$1<=m&CNID`(lvSZ zG?SdSCggxHMfvA8|J`8_d~auG+{DPFAc#WAE)533Qds%<4_&ChqAsC6v1E|Aqka0s ztE~Uc(eI=Iz*EVWfy3w4=C~Vhhx+se)WZ~@XYwrK*#KFhV+tGW#v8x^=H+G*bJN#I z?Ir%4@J2&lypPYC$b)Qh2q6wiUzp{{q~uBT=^Ji>M@ZA9gAeliJM&p@y@B`sRy7TH zDYCET-vk)!bwp=-CGX}_!*8@+r}=)!eAhWu+A|K6s67GYg%TC2OT!rSBO{ z`T6BBkPxNG4vqj|W$dFCtA=xnz^I!45`_PfN`C03iJT&Nh%F`^QL6zT^!k(k!Tem; zZ@S@=PHZf;J8`1Lzf2*KtI}B;GEec|yl84p#W^Jyay{%I2IIOp_TuW;;!!^Rzl&+q zobBR?YwJiC;cr7K;yk8+=mzKA$CRAgz%P&d0EM05ItE+i;Pl#bdCS-teQSN|`r|u6 zgLpaMTld5)m{|V+k*3mQc~np8q%-(w*z&4oaV!uLqdJhog1POirIZ74rrJ91B%)z~ zpuq(-adFg`HL$Bj5}CL9n-N);GV||Go&gK@U9cHvp$|pfBTPSFOPXT1d6U`xZL;gP z@&bM=*-eesY4C!Mz~XEN&z9nE3bJ?i1s|z+`Em^1-uHywgzP z{COlUAj0vV=|KM9p6dRPv;3{amldPWxramY*XOSmTjUfZPtMLm`QANJpe(tu-rG%x zgO}s>Z{F<T&Ey#RF5u0YtC9U3F;+9z8Y-tKIX8zCn`ZC1*VD(10;^UUPE}5 z>-!*`1yidZf2l;Cr<)e9pu@rMZv{48M8D#`le?OGiZ7BM**BHjx6m_Ty&1va{)_P2 zL-Fv@-9@Q>*Z`|2*?x#CTv74Us7vB+lG35B#KE_?1nH@v4(%GZa}_@$LPq!`KiG9i zTYmnw*laeEmTcR6HJT~PXOpMg{}<{95dgUH!Zk;CC{|-rRrCuiy4@rkR7)Q(Xnzzt zo7gq0qQv zo@Vpx_QCOwHPfG1Gk1wc`2k$+2)~9*03wN5oCk6HOvEb%Q-ZK};LRWehsy*;-VMiL z%al+6=`TBs2uOyLTHD&r)R?|W62k&*k=qdiL3d(NL?k@Qq9oS|jla5E$Y*901J?J| z@{)1=-gB}qqY6LAa9oPY2uP8+%px!bDFC52XI6t(1er z{4|e3uOft~J;S$rIl)dM~JH*D!- zBibc9|1+T40XFQ;{p6KDB{~$rZmEAK(U<0%jZcYd<`BTWMyU!J3-$bp#DnnuL3zCU z??=T(_i6PFrqu@2E_>^^#a6j>X;{SO%=Kd;pg=+8~q|bn|xRl18A|n&v)8K5(*r~@XlnKo$6wG zLl(ZSO|NN|Yt1~<>sfCXlxti$3sCCu4SzVZh&WvSSGw4Z-f5c5vOUEXDaH%GwX1Py zG~isS@3cufaPZ|npJ;#pGtsaZEA6t^6T(mXq9m%Th~VE9zv!}~|2PlvpLhPAB0!^? zi6Zw{NFS(?-j#jUlu0^;nU^=j6;9TxFX1a*l7Mizef96sDXXI&op3`@z7 zQlj(7bJHa`_5W(`JENNVzHI|2O#vwiN|P!INR=9jbOGtTh*G6SYJkubq=cd%RUmW_ zr3gqTC`gTTDN;j`-n;a>PTz#V?lZ?`3*_;8@r#HwU&yO0r12fU9tAJ zZ`q8Cqdo^|w&>QrCJ4r}2u#|0FW025_Uk2!@;%ga-YPpQE7+VOiU3>1Z#1(8Sti8^ z*>o9BaqTe)`I!;AS*P_@>s2(>`ZaMw7~*>2GD>%ED2?Wp0<@Rps9?gh%7@UnpC}(A zd=*@lN&H=z%NfKm2&=*_W)Z{!tyf>g3K<~l3{otCkU_6X8Id1wqZdzKhN=O#rQKir zVq#p&^YQa-PXTZa#eqT&$s1dW8pS>JXLrlR{qD30t91X>{2!MQ0|vI{Y?tytp$QoP zKNm6)LGd{Wjc;z1a(X9d}IlEHz-0pf9)5 z9!S>lPUG05QA-eE8qmGuyPv<@{z}kT|9f2FVJvRzhE$~UlYzy;lG=YRV|0r zz7GZHU-pXN%r&UCNaYN**}aO}LWk}f3Qc?Mah49&gsVp_N}X`o2-9!i>mKSNasiC| zVSHCew|dh*B+tFBxcC<31@v5O$N_Ul8F|{)i-6WggloyYu}B+cx{9-GZ69gMtE$4t zb$E-VfMs2H2WIRp4O0BrI9qk99QpSGeh6TxwY<6(=?)Q-I2vW+b_|!0UB9RuM>%S% z1-(n@rSZ|oc=#nXlgrd9Iprt=xoJvvqq;Wmee)g1qP4ed8}NEY>6t6JA+Ox=NP7^I z0U+XH>+*^3swpCHmv))hB#YHrj2LLoxI8yITl~S>syQImKNk~o#89>-np19tXTg86 z0jL>q$NqY&2$17qqpQa4Sp7OVz+i?K_Q*wF@jLFOZ#4q4A{yk;el+`=epfvv?)ZO| z7ypWaZ#4>DOV>@271BqxmRq&Q40jU#XO1A%1F#$6=4ju_){`YrC9%IUqoAxc+!A665$}ujePJVlA>~J>dg=x z-hJEXun$5cYfsLSWpIYFlV)`u%iHbDhFER!F6c2|ErbB>v7+%JX= z)wA@;UGwbQyT$s~#{YD$K<4^!JIsI86xOhb84ku?6W{d*Zjn%jnR@iq{AbGfrvYKo#goC7-Q7$(Tfr9T@$Xq~v_O5zB(76z zYY(neNL78H>{&6><9x8s@6)VP`%9@S^)jFnK+t36?efiJ=p)GP8t=$u^p{oO zVXk+#{}-OO<$-U9^M06b!JCR zQU!vG37Hq}gp$o)ou_W{2?SEBNEpl|>Uyz5r6A3sk7cm}Q2l0JwAApja#QxYx9pj= z?wjsD0BZoNB^RZ~iIu`{csoPm)@@2kW|EQ{4$+Q(BbXj&TwHeJkHywTWrn~;u z&9!VFz);=5*sfwp>kK=aUX=ZMOg_GwCZ#LGe6u%Rf9#qt3HOgN=PV}+UAGpX{N3}R z(+GbUw%GD3NS}t4eItGcNR!ffbmG~TwT8|jy?qmq)ClniEtusbx@3x}{WrV(#Bxeu zVG1hmvihnM#2cTKjO9i_Q|${ehAk}>?{DA!cWI69JzwjAq#%V9HNAlHh0#}eZ&nO| zlgfcJtrj0TX+g%}o#VTT3N8N*%)2}vX5;m}xEI9=aB>;4WYUHSn)g>nOQxg85L!r2 z#ecv4{!)h6b=A@2`*BjU|4Y;>_;}|LfWR{>e&5<#PG7&7R(!KWAb;R3U1IX-tK}Ls zH8o(Lp3$9b{eF&JIJSolH~~nP#RBs7RvU-j*Oop8VYu5BkUZ7ue7&fzl{k`$UV#3m zq7x^<KA*n;@)+on)}oUafOw!(Ymun#!3pItdLREP6DQ%4It zC1$#1*tHomfBxAjktJpiMC>tz<|}#sVrJS#^xe~2S~}oz3$euC3?*s=l3we z_l~$$ES>ZwJ#i^|qEuEq&MgdQRyLx!?+j$h+g$%Nr%-yyL@=c!F>E`Ki~S$lxk3WX zq^8|QovDi#QMH@j!UPb#X@3BfaHlLfS(FTl_d3*O_foS@t9*_D_d8_9*mQ^{Lu^9j zcujP4wt)U^*81<2wR9#RqdBsar*xlDJZ(8;ZoA7P;7E{6lLc915GuA^JxLRg@rtSn zOF15{AYyrR#Dtmo7&$Cm-ez@iajH^0K{KTY8-6;BT1e%+AuFm`2{|&Y(x*LoxD9$yD%k@5uo$3N9`;bHe! zT;iPtv8+a~ls|niE)|Tc?c_W`)(a*Wqi+2`!Y>wIzN|L9qBa!7fLxg=8cexO96g~f z@gq{#IQBE8Xmd>U$%kirG0_Q|uVBo+4({$)PToY5h`jfkhVYC;_wu`>(L6klY<(P@ zv=!K$*poPOSOwjLHfo4old(-qClFmN%iUS+0Rv!Mcef4BI(mcgMbEpepF=kQ+#Ol7>O*G3x9)ab(WCfG$tbmd+A63%o~}j269{Q5xmk<$byN ziU>alrtan0Q|;#7X4)Q+SMv6zX}onz@AQ~_k7)>>Jh$2wh*-3c9Nv(RXItMX;G0uY zHU8e-e=rH$E2|Ih-1dBqXd)FC%7o}4KyIl8V!aaUhz5BtfOsjv=4Ck_Wz;QM$Lzz? zI4|S_7=%GPN%w^=yA1y-*xc6ER*F!RiWqM$xig;;3#|1$m2Q;qh2SMuRu%Ke+lvJS z`|>?kxgau({^6H2NDJl19naqW3<5bKG?b zn8aa}jUj1-G{ix}7V1r%vr#$JZ+A7?9;^KIp`$DalQU_^a1vB}Hcp`l(gCBwxt9>9 zhpG1ebAnsA8bdBc0$e3t-XBi(+x)9SnE+PqG)FRY4xEl_ax#4;Azp1mp^=ZV4Q> zM$&@ccI!z!!5}h!;s$Y;{s!ky{2&1ni8bb|oPb{jZ4qMnr@K}7UwiP$xPaH7>N5Vv zd*O(xZJFkWjQiiK1T|U;XEUm=_XXiEiD{ysMZp$Y}66i?0$v1j**AerQ#X`mlSpxT&B0vB}xM*Sbhnb$Tp0xr%D6T+~8 z7iSwAC2#gYIW6#F=Gg9H@7L3vAO7^4Q|Ob^BX^4xJP22tp)0=DdOLIA>29wKm<(4@8vS+5KS2H=^{{2GBY2DAKv@nX^hT`KRCLdAOUsmfHAQ!G z)dnU!Gx$;Ho zPlAnAwb>CB(y=OC7Eb)~4}|M)j5NqNnxLid6^4wtt=n82)vmA=Cs8aePByB!Qo1s& zDO^4DlUSu*j_z0+LzX<~mD4qGvJo=a|Ai8TnIlCJjCTI4WgPN`Y~}82U&+E86AyF( zd@(n>A1hrSxg`AUDwoMa79o+uyMr#=^GOAXS2zMmKU$TdbOMfLEAgJ1()-VCCv&o9 zx4K;I-01ZTID#{+?U@f8d??xBNK-|s9(C;{J4(ERrHe5I?>@cbroDShyY!;rYo(BI z>c9iaeoLV_fBNcsLJy)MwkbSEU}b%m$3X|9CH z*%l4&mFei%@fS|&VKfcV*6TUJd|szth&+V9quSpv!$h|JY+HT(&r*5!nw3; zvVQzxBGo>B+Q2=3#7EhGe}5hNLg7m=qH`mywvLU}U%o=fhm+c8VQKCQTLG|3Tx{HL6+6RqQZrcy{FXeg(us)$n+mC2*u=aA+EluZy zeL*AL10C&_+AvSdM5?U4Y=!c~S9c@!TJkCrj52cuLt>$wdLh}qV)f?9QA&!rdY>b8 z;^pkwX{chuGz5yUPQpK!_5@ZeEW0&xMZFY`L)+|hTw2ohk7|da8Dkq8x9Pdd{KYF5 z7aIsd6@JGtKgcLTp->qC$IH|0zZ6+x9Y;!zbOtE4gJG0YJ_wd zi=ltfSqNAl$tWo71XETJn8|36b|!tpiz{)Ok~oofgx0NgLdUH9hYy_5&!@jX$*tDR zNfG9&2e~QR<5hBrq|y& zp#$Yn5==^sQX-1VUcc{?Kq#Qiyk9_WXF9EsyKHuKg$+iJzw;FI_mK!)hlfcmb zv9#byG|%rFN^;Qy^Nd4Wh{UTCW+po4oG?>*`Oy2b6WBPl1a5>(%~*9Ts+tiONbQBj z(=IB$!{{I^D;-c@`n^V8&L7@NxI0YV&(Cr~Wh9iW$M{vWgrZ#ke&sA4u$pzbj z4!PZS@m~1!55%d}nr$ouEzodlGL!{DZVK*U^c5vd4;kUCQ}uNUhav_CUK#rV0t3-3 z8AxeYG#cIiy!)o#Ci>79fjIlP82((AlDO@Iq{VeoADHWF$x%xiD?JL@^e_Y>iPWo~ z&eN_$u0OV4HHmBN+j(^Vs@jz)6xzfnO{dVW;c%91Bq@#{`doPmQQ*DwMgGG0)8ujJ zT%HP(0ov8jKCL9bH*{_!#{u@teN$T#)}b`EehY3uJ)b&Q`KxQK&qd9I|l@OUbLi&!e zxUfw^H1GZQNYSfah&GoDkY$cRXh_&{rAk zLIr4szIzqwy};xkZ&X!XA<&WRV2E-VQhSbf-Vl>iwmEySBQ|m_^;}c4x6*N>@py%s zQs8k#K|%W_lDz#Y7d|(G1!ldl@u1vCQ!D3f3Iy6dK2FphSD2%D#FbksG;6cL+hO-+z*WTC zpca$9Id3|1D6{!%mwI^;vzF&o2=0K!^>DF+FK!K)ZydL)YbpDWPjh({{#mXL#-)zQvI8`Jt4SqDu+jjB6qXZ=O~!(&^8 zOS;*s$EGm!{sG{9F3wK^M%>tiGl}z;g0%Na=BV#p2RAe{@mWDQ%7i|tIK=*a#GZk*SKF%inv;b8 zC%=?U=N{iqQy|PH zQ0gyND4oD7h_una50Pqy7BkUgz#~$IwLS@%) zW<=Ptf?2h`68sl9Racd4sG)A8rg#QB^%y6G9$2VRIRP<|k%{Si@24h+z*gbB%FR?z z6UA=dxY(}8B3LPj+W0OW&1jp}`u;DzAr$|CSJ)Y1?$9d9u~H~1tgX+lS!x6P0*eVz zkbC#Y%GcFFo!zO$^Sfyw2@Vg<(~8EOY%q{0Dw82xow`iDBgyG3#f!0l$nYC##VtA~ zA}fX6-7EeFiM9kw?7DiXIUm^v)eI-C94Z!xq$&#|!=INWBm`>?N%mIW96d=pQW`&8 zEI8PVW(+rd7d)mkd37-3?SyFZ+VW7d&QMJ#*qFKXsSCr%f*ut>OKzIf7#ByA5<~cg zh~y0H(zuD-E+Dk;4t5T|UQBLM#Q;#G+|Fg)c4J;xX;jYE2{G-V3~Lk^9TLZ4x^Kc5 z@iZq3$^7oTOsghtyuw7BsdHXcSuIEi3E;NQM=KtIj~rLEo-6V+5nR-Pc={oJ8v!_$*?r?9N+uB1X&nF_<+lMmpcC&R%T<#s2~u;3P}Lc0FlFYZbi*&J0X|*!Bk@H{o|F2(tof?D_dQ<~w*( zVV|{<0*{M%E{pePZtjiD#>U2tSn#GPebllErpgM4+PB()hAb{7#>E;O987!PY^$CD z5+niOiJy>|*a!Rfa6uP2U>2lf_7DC6*Ze;RP5;w*X}Ad)RZ@D|0>rCj%drZrfR`AEoA_w>LK%=HMK-L&SFb7k|cc+%FE=MlTRy zOgQXYuvWj>D0za7ow|d*IKo_Peptide Annotator. @@ -709,6 +709,7 @@ We can select **meta** **groups** or **samples** (default a - The yellow dots are taxa, and the grey dots are functions, the size of the dots presents the intensity - The red dots are the taxa we focused on - The green dots are the functions we focused on +- More parameters can be set in **Dev**->**Settings**->**Others** (e.g. Nodes Shape, color, Line Style) taxa_func_network diff --git a/utils/GUI.py b/utils/GUI.py index e7dfee7..6be1fd1 100644 --- a/utils/GUI.py +++ b/utils/GUI.py @@ -129,9 +129,6 @@ def __init__(self, MainWindow): # Check and load settings self.load_basic_Settings() - # set the default theme mode - self.theme = 'white' - #check update self.update_required = False self.check_update(manual_check_trigger=False) @@ -161,6 +158,9 @@ def __init__(self, MainWindow): self.add_theme_to_combobox() # ploting parameters + # set the default theme mode + self.html_theme = 'white' + self.heatmap_params_dict = {'linkage_method': 'average', 'distance_metric': 'euclidean'} self.tf_link_net_params_dict = {'taxa_shape': 'circle', 'func_shape': 'rect', @@ -656,6 +656,7 @@ def show_settings_window(self): settings_widget.auto_check_update_changed.connect(self.on_auto_check_update_changed) settings_widget.heatmap_params_dict_changed.connect(self.on_heatmap_params_changed) settings_widget.tf_link_net_params_dict_changed.connect(self.on_tf_link_net_params_changed) + settings_widget.html_theme_changed.connect(self.on_html_theme_changed) layout.addWidget(settings_widget) self.settings_dialog.setLayout(layout) @@ -682,6 +683,10 @@ def on_tf_link_net_params_changed(self, params_dict): self.tf_link_net_params_dict = params_dict print(f"Taxa-func link network params changed to: {params_dict}") + def on_html_theme_changed(self, theme): + self.html_theme = theme + print(f"HTML theme changed to: {theme}") + ############### basic function End ############### @@ -722,9 +727,11 @@ def change_theme(self, theme, silent=False): self.show_message(f"Changing theme to {theme}...") # save the theme to settings self.settings.setValue("theme", theme) - # save the theme mode to GUI attribute (dark or light) - self.theme = 'dark' if 'dark' in theme else 'white' - print(f"Theme mode: {self.theme}") + + #! Deprecated, switch to manual change in Settings + ## save the theme mode to GUI attribute (dark or light) + # self.html_theme = 'dark' if 'dark' in theme else 'white' + # print(f"Theme mode: {self.html_theme}") # recover the .xml suffix theme = theme + '.xml' @@ -3396,7 +3403,7 @@ def plot_basic_list(self, plot_type='heatmap'): if reply == QMessageBox.No: return None self.show_message(f'Plotting {plot_type}...') - pic = BarPlot_js(self.tfa, theme=self.theme).plot_intensity_bar(df = df, width=width, height=height, + pic = BarPlot_js(self.tfa, theme=self.html_theme).plot_intensity_bar(df = df, width=width, height=height, title= '', rename_taxa=rename_taxa, show_legend=show_legend, font_size=font_size, rename_sample=rename_sample, plot_mean = plot_mean, @@ -3432,7 +3439,7 @@ def plot_basic_list(self, plot_type='heatmap'): else: title_new = '' subtitle = '' - pic = SankeyPlot(self.tfa, theme=self.theme).plot_intensity_sankey(df=df, width=width, height=height, + pic = SankeyPlot(self.tfa, theme=self.html_theme).plot_intensity_sankey(df=df, width=width, height=height, title=title_new, subtitle=subtitle, font_size=font_size, show_legend=self.checkBox_basic_bar_show_legend.isChecked()) self.save_and_show_js_plot(pic, title) @@ -3717,7 +3724,7 @@ def plot_trends_interactive_line(self): try: - pic = TrendsPlot_js(self.tfa, theme=self.theme).plot_trends_js( df=df, width=width, height= height, title=title, + pic = TrendsPlot_js(self.tfa, theme=self.html_theme).plot_trends_js( df=df, width=width, height= height, title=title, rename_taxa=rename_taxa, show_legend=show_legend, add_group_name = plot_samples, font_size=font_size) self.save_and_show_js_plot(pic, f'Cluster {cluster_num+1} of {table_name}') @@ -3792,7 +3799,7 @@ def save_and_show_js_plot(self, pic, title, width=None, height=None): pic.render(save_path) self.logger.write_log(f'html saved: {save_path}', 'i') - web = webDialog.WebDialog(save_path, None, theme=self.theme) + web = webDialog.WebDialog(save_path, None, theme=self.html_theme) if title: web.setWindowTitle(title) @@ -4036,7 +4043,7 @@ def get_title_by_table_name(self, table_name): return None self.show_message('PCA is running, please wait...') pic = PcaPlot_js(self.tfa, - theme=self.theme + theme=self.html_theme ).plot_pca_pyecharts_3d(df=df, title_name=title_name, show_label = show_label, rename_sample = rename_sample, width=width, height=height, font_size=font_size, legend_col_num=legend_col_num) @@ -4094,7 +4101,7 @@ def get_title_by_table_name(self, table_name): else: show_label = False - pic = SunburstPlot(theme=self.theme).create_sunburst_chart(taxa_df= taxa_df, width=width, height=height, + pic = SunburstPlot(theme=self.html_theme).create_sunburst_chart(taxa_df= taxa_df, width=width, height=height, title='Sunburst of Taxa', show_label=show_label, label_font_size = font_size) self.save_and_show_js_plot(pic, 'Sunburst of Taxa') @@ -4106,7 +4113,7 @@ def get_title_by_table_name(self, table_name): taxa_df = self.tfa.taxa_df[sample_list] - pic = TreeMapPlot(theme=self.theme).create_treemap_chart(taxa_df= taxa_df, width=width, height=height, + pic = TreeMapPlot(theme=self.html_theme).create_treemap_chart(taxa_df= taxa_df, width=width, height=height, show_sub_title = self.checkBox_pca_if_show_lable.isChecked(), font_size = font_size) self.save_and_show_js_plot(pic, 'Treemap of Taxa') @@ -4120,7 +4127,7 @@ def get_title_by_table_name(self, table_name): df = df[sample_list] title = 'Sankey of Taxa' if table_name == 'Taxa' else 'Sankey of Taxa-Functions' - pic = SankeyPlot(self.tfa, theme=self.theme).plot_intensity_sankey(df=df, width=width, height=height, + pic = SankeyPlot(self.tfa, theme=self.html_theme).plot_intensity_sankey(df=df, width=width, height=height, font_size = font_size, title='', subtitle='') self.save_and_show_js_plot(pic, title) @@ -4789,7 +4796,7 @@ def plot_deseq2_volcano(self): # VolcanoPlot().plot_volcano(df, padj = pvalue, log2fc = log2fc, title_name='2 groups', width=width, height=height) try: df = self.table_dict[table_name] - pic = VolcanoPlot(theme=self.theme).plot_volcano_js(df, pvalue = pvalue, p_type = p_type, + pic = VolcanoPlot(theme=self.html_theme).plot_volcano_js(df, pvalue = pvalue, p_type = p_type, log2fc_min = log2fc_min, log2fc_max=log2fc_max, title_name=title_name, font_size = font_size, width=width, height=height) @@ -4842,7 +4849,7 @@ def plot_co_expr_network(self): show_labels=show_labels, rename_taxa=rename_taxa, font_size=font_size, - theme=self.theme, + theme=self.html_theme, **self.tf_link_net_params_dict ).plot_co_expression_network(df_type= df_type, corr_method=corr_method, corr_threshold=corr_threshold, sample_list=sample_list, width=width, height=height, focus_list=focus_list, plot_list_only=plot_list_only) @@ -4889,7 +4896,7 @@ def deseq2_plot_sankey(self): df = self.table_dict[table_name] title_name = f'{group1} vs {group2} of {table_name.split("(")[1].split(")")[0]}' - pic = SankeyPlot(self.tfa, theme=self.theme).plot_fc_sankey(df, width=width, height=height, pvalue=pvalue, p_type = p_type, + pic = SankeyPlot(self.tfa, theme=self.html_theme).plot_fc_sankey(df, width=width, height=height, pvalue=pvalue, p_type = p_type, log2fc_min=log2fc_min, log2fc_max=log2fc_max, title =title_name, font_size=font_size) self.save_and_show_js_plot(pic, f'Sankay plot {title_name}') @@ -5075,7 +5082,7 @@ def plot_network(self): show_labels=show_labels, rename_taxa=rename_taxa, font_size=font_size, - theme=self.theme, + theme=self.html_theme, **self.tf_link_net_params_dict ).plot_tflink_network(sample_list=sample_list, width=width, height=height, focus_list=focus_list,plot_list_only=plot_list_only) self.save_and_show_js_plot(pic, 'taxa-func link Network') @@ -5354,7 +5361,7 @@ def plot_tflink_bar(self): params['show_all_labels'] = show_all_labels self.show_message('Plotting bar plot, please wait...') - pic = BarPlot_js(self.tfa, theme=self.theme).plot_intensity_bar(**params) + pic = BarPlot_js(self.tfa, theme=self.html_theme).plot_intensity_bar(**params) self.save_and_show_js_plot(pic, 'Intensity Bar Plot') diff --git a/utils/MetaX_GUI/Setting.ui b/utils/MetaX_GUI/Setting.ui index 1f01903..6f5ccba 100644 --- a/utils/MetaX_GUI/Setting.ui +++ b/utils/MetaX_GUI/Setting.ui @@ -78,181 +78,173 @@ Others - - - - Taxa-Functions Link Network - - - - - - - HeatMap - - - - - + + + + + + + 0 + 0 + + + + Qt::LeftToRight + + + Linkage Method + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + - + + + average + - circle + average - rect + single - roundRect + complete - triangle + centroid - diamond + median - pin + weighted - arrow + ward - - + + + + + 0 + 0 + + - Taxa Shape + Linkage Metric + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + + + euclidean + - bold + euclidean - normal + chebyshev - bolder + cityblock - lighter + hamming + + + + + matching + + + + + minkowski + + + + + rogerstanimoto + + + + + russellrao + + + + + sqeuclidean - - - - #374E55 - - - - - - - Repulsion - - - - - - - Focus Taxa Color - - - - - - - Func Color - - - - - - - Line Curve - - - - - - - #9aa7b1 - - - - - + + + + + + Taxa-Functions Link Network + + + + + + + Network Global + + + + + + + - #DF8F44 - - - - - - - 1 - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.000000000000000 - - - - - - - 100000 - - - 10 - - - 500 + Taxa Shape - - + + - Font Weight + #B24745 - - + + - #B24745 + #6A6599 @@ -263,19 +255,10 @@ - - - - 1 - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.500000000000000 + + + + #374E55 @@ -318,10 +301,10 @@ - - + + - Line Color + Focus Taxa Color @@ -332,125 +315,102 @@ - - + + - Line opacity + Function Shape - - + + - #6A6599 + #DF8F44 - - + + - Line Width + Func Color - - - - 1 - - - 0.100000000000000 - - - 1.000000000000000 - - - 3.000000000000000 - - - - - - - Function Shape - - - - - - - Label Postion - - - - - + + - bottom + circle - top + rect - right + roundRect - left + triangle - inside + diamond - insideLeft + pin - insideRight + arrow - - + + - Text Width + Line Color - - - - 9999 - - - 10 - - - 300 + + + + #9aa7b1 - - + + + + + + + + + 0 + 0 + + - Gravity + Line Width - - + + + + 1 + 1.000000000000000 @@ -458,139 +418,252 @@ 0.100000000000000 - 0.200000000000000 + 0.000000000000000 - - - - - - + + + + The larger the value the greater the repulsion + + + Repulsion + + + + + + + Line opacity + + + + + 0 0 - - Qt::LeftToRight + + Line Curve + + + + - Linkage Method + Text Width - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 100000 + + + 10 + + + 500 - - - - average + + + + 1 + + 1.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + + Font Weight + + + + + - average - - - - - single - - - - - complete - - - - - centroid + bold - median + normal - weighted + bolder - ward + lighter - - + + - + 0 0 - Linkage Metric + Label Postion - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 9999 + + + 10 + + + 300 - - - - euclidean + + + + + 0 + 0 + + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + 3.000000000000000 + + + + + - euclidean + bottom - chebyshev + top - cityblock + right - hamming + left - matching + inside - minkowski + insideLeft - rogerstanimoto + insideRight + + + + + + The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre. + + + Gravity + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.200000000000000 + + + + + + + + + HeatMap + + + + + + + HTML Global + + + + + + + + + Theme + + + + + - russellrao + white - sqeuclidean + dark diff --git a/utils/MetaX_GUI/Settings.py b/utils/MetaX_GUI/Settings.py index 3da8e40..667073d 100644 --- a/utils/MetaX_GUI/Settings.py +++ b/utils/MetaX_GUI/Settings.py @@ -7,6 +7,7 @@ class SettingsWidget(QWidget): auto_check_update_changed = pyqtSignal(bool) heatmap_params_dict_changed = pyqtSignal(dict) tf_link_net_params_dict_changed = pyqtSignal(dict) + html_theme_changed = pyqtSignal(str) def __init__(self, parent=None, update_branch="main", auto_check_update=True): super().__init__(parent) @@ -55,6 +56,9 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True): self.ui.spinBox_tf_link_net_text_width.valueChanged.connect(self.handle_tf_link_network_changed) self.ui.doubleSpinBox_tf_link_net_gravity.valueChanged.connect(self.handle_tf_link_network_changed) + # HTML theme + self.ui.comboBox_html_theme.currentTextChanged.connect(self.handle_html_theme_changed) + def init_ui(self, update_mode, auto_check_update): if update_mode == "main": @@ -110,7 +114,11 @@ def handle_radio_button_toggled(self, checked): self.update_mode = "dev" self.update_mode_changed.emit(self.update_mode) - + def handle_html_theme_changed(self): + theme = self.ui.comboBox_html_theme.currentText() + self.html_theme_changed.emit(theme) + + if __name__ == "__main__": import sys from PyQt5.QtWidgets import QApplication diff --git a/utils/MetaX_GUI/Ui_Setting.py b/utils/MetaX_GUI/Ui_Setting.py index bde7ffc..177380a 100644 --- a/utils/MetaX_GUI/Ui_Setting.py +++ b/utils/MetaX_GUI/Ui_Setting.py @@ -44,122 +44,198 @@ def setupUi(self, Settings): self.page_2.setObjectName("page_2") self.gridLayout_4 = QtWidgets.QGridLayout(self.page_2) self.gridLayout_4.setObjectName("gridLayout_4") + self.gridLayout_8 = QtWidgets.QGridLayout() + self.gridLayout_8.setObjectName("gridLayout_8") + self.label_2 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) + self.label_2.setSizePolicy(sizePolicy) + self.label_2.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_2.setObjectName("label_2") + self.gridLayout_8.addWidget(self.label_2, 0, 0, 1, 1) + self.comboBox_heatmap_linkage_method = QtWidgets.QComboBox(self.page_2) + self.comboBox_heatmap_linkage_method.setObjectName("comboBox_heatmap_linkage_method") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_method, 0, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) + self.label_3.setSizePolicy(sizePolicy) + self.label_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_3.setObjectName("label_3") + self.gridLayout_8.addWidget(self.label_3, 0, 2, 1, 1) + self.comboBox_heatmap_linkage_metric = QtWidgets.QComboBox(self.page_2) + self.comboBox_heatmap_linkage_metric.setObjectName("comboBox_heatmap_linkage_metric") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_metric, 0, 3, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_8, 0, 3, 1, 1) self.label_4 = QtWidgets.QLabel(self.page_2) self.label_4.setObjectName("label_4") - self.gridLayout_4.addWidget(self.label_4, 2, 0, 1, 1) - self.label = QtWidgets.QLabel(self.page_2) - self.label.setObjectName("label") - self.gridLayout_4.addWidget(self.label, 0, 0, 1, 1) + self.gridLayout_4.addWidget(self.label_4, 3, 0, 1, 1) + self.label_20 = QtWidgets.QLabel(self.page_2) + self.label_20.setObjectName("label_20") + self.gridLayout_4.addWidget(self.label_20, 2, 0, 1, 1) self.gridLayout_6 = QtWidgets.QGridLayout() self.gridLayout_6.setObjectName("gridLayout_6") - self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_taxa_sahpe.setObjectName("comboBox_tf_link_net_taxa_sahpe") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_taxa_sahpe, 0, 1, 1, 1) self.label_5 = QtWidgets.QLabel(self.page_2) self.label_5.setObjectName("label_5") self.gridLayout_6.addWidget(self.label_5, 0, 0, 1, 1) - self.comboBox_tf_link_net_font_weight = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_font_weight.setObjectName("comboBox_tf_link_net_font_weight") - self.comboBox_tf_link_net_font_weight.addItem("") - self.comboBox_tf_link_net_font_weight.addItem("") - self.comboBox_tf_link_net_font_weight.addItem("") - self.comboBox_tf_link_net_font_weight.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_font_weight, 3, 5, 1, 1) + self.lineEdit_tf_link_net_func_focus_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_func_focus_color.setObjectName("lineEdit_tf_link_net_func_focus_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_focus_color, 1, 5, 1, 1) + self.lineEdit_tf_link_net_taxa_focus_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_taxa_focus_color.setObjectName("lineEdit_tf_link_net_taxa_focus_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_focus_color, 0, 5, 1, 1) + self.label_8 = QtWidgets.QLabel(self.page_2) + self.label_8.setObjectName("label_8") + self.gridLayout_6.addWidget(self.label_8, 1, 4, 1, 1) self.lineEdit_tf_link_net_taxa_color = QtWidgets.QLineEdit(self.page_2) self.lineEdit_tf_link_net_taxa_color.setObjectName("lineEdit_tf_link_net_taxa_color") self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_color, 0, 3, 1, 1) - self.label_15 = QtWidgets.QLabel(self.page_2) - self.label_15.setObjectName("label_15") - self.gridLayout_6.addWidget(self.label_15, 2, 4, 1, 1) + self.comboBox_tf_link_net_func_shape = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_func_shape.setObjectName("comboBox_tf_link_net_func_shape") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_func_shape, 1, 1, 1, 1) self.label_9 = QtWidgets.QLabel(self.page_2) self.label_9.setObjectName("label_9") self.gridLayout_6.addWidget(self.label_9, 0, 4, 1, 1) + self.label_7 = QtWidgets.QLabel(self.page_2) + self.label_7.setObjectName("label_7") + self.gridLayout_6.addWidget(self.label_7, 0, 2, 1, 1) + self.label_6 = QtWidgets.QLabel(self.page_2) + self.label_6.setObjectName("label_6") + self.gridLayout_6.addWidget(self.label_6, 1, 0, 1, 1) + self.lineEdit_tf_link_net_func_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_func_color.setObjectName("lineEdit_tf_link_net_func_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_color, 1, 3, 1, 1) self.label_10 = QtWidgets.QLabel(self.page_2) self.label_10.setObjectName("label_10") self.gridLayout_6.addWidget(self.label_10, 1, 2, 1, 1) - self.label_14 = QtWidgets.QLabel(self.page_2) - self.label_14.setObjectName("label_14") - self.gridLayout_6.addWidget(self.label_14, 2, 2, 1, 1) + self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_taxa_sahpe.setObjectName("comboBox_tf_link_net_taxa_sahpe") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_taxa_sahpe, 0, 1, 1, 1) + self.label_13 = QtWidgets.QLabel(self.page_2) + self.label_13.setObjectName("label_13") + self.gridLayout_6.addWidget(self.label_13, 2, 0, 1, 1) self.lineEdit_tf_link_net_line_color = QtWidgets.QLineEdit(self.page_2) self.lineEdit_tf_link_net_line_color.setObjectName("lineEdit_tf_link_net_line_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 3, 3, 1, 1) - self.lineEdit_tf_link_net_func_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_func_color.setObjectName("lineEdit_tf_link_net_func_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_color, 1, 3, 1, 1) + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 2, 1, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_6, 3, 3, 1, 1) + self.gridLayout_7 = QtWidgets.QGridLayout() + self.gridLayout_7.setObjectName("gridLayout_7") + self.label_11 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_11.sizePolicy().hasHeightForWidth()) + self.label_11.setSizePolicy(sizePolicy) + self.label_11.setObjectName("label_11") + self.gridLayout_7.addWidget(self.label_11, 0, 0, 1, 1) self.doubleSpinBox_tf_link_net_line_curve = QtWidgets.QDoubleSpinBox(self.page_2) self.doubleSpinBox_tf_link_net_line_curve.setDecimals(1) self.doubleSpinBox_tf_link_net_line_curve.setMaximum(1.0) self.doubleSpinBox_tf_link_net_line_curve.setSingleStep(0.1) self.doubleSpinBox_tf_link_net_line_curve.setProperty("value", 0.0) self.doubleSpinBox_tf_link_net_line_curve.setObjectName("doubleSpinBox_tf_link_net_line_curve") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_line_curve, 2, 3, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_line_curve, 0, 3, 1, 1) + self.label_15 = QtWidgets.QLabel(self.page_2) + self.label_15.setObjectName("label_15") + self.gridLayout_7.addWidget(self.label_15, 1, 0, 1, 1) + self.label_12 = QtWidgets.QLabel(self.page_2) + self.label_12.setObjectName("label_12") + self.gridLayout_7.addWidget(self.label_12, 0, 4, 1, 1) + self.label_14 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_14.sizePolicy().hasHeightForWidth()) + self.label_14.setSizePolicy(sizePolicy) + self.label_14.setObjectName("label_14") + self.gridLayout_7.addWidget(self.label_14, 0, 2, 1, 1) + self.label_18 = QtWidgets.QLabel(self.page_2) + self.label_18.setObjectName("label_18") + self.gridLayout_7.addWidget(self.label_18, 2, 0, 1, 1) self.spinBox_tf_link_net_repulsion = QtWidgets.QSpinBox(self.page_2) self.spinBox_tf_link_net_repulsion.setMaximum(100000) self.spinBox_tf_link_net_repulsion.setSingleStep(10) self.spinBox_tf_link_net_repulsion.setProperty("value", 500) self.spinBox_tf_link_net_repulsion.setObjectName("spinBox_tf_link_net_repulsion") - self.gridLayout_6.addWidget(self.spinBox_tf_link_net_repulsion, 2, 5, 1, 1) - self.label_16 = QtWidgets.QLabel(self.page_2) - self.label_16.setObjectName("label_16") - self.gridLayout_6.addWidget(self.label_16, 3, 4, 1, 1) - self.lineEdit_tf_link_net_func_focus_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_func_focus_color.setObjectName("lineEdit_tf_link_net_func_focus_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_focus_color, 1, 5, 1, 1) - self.label_8 = QtWidgets.QLabel(self.page_2) - self.label_8.setObjectName("label_8") - self.gridLayout_6.addWidget(self.label_8, 1, 4, 1, 1) + self.gridLayout_7.addWidget(self.spinBox_tf_link_net_repulsion, 1, 1, 1, 1) self.doubleSpinBox_tf_link_net_line_opacity = QtWidgets.QDoubleSpinBox(self.page_2) self.doubleSpinBox_tf_link_net_line_opacity.setDecimals(1) self.doubleSpinBox_tf_link_net_line_opacity.setMaximum(1.0) self.doubleSpinBox_tf_link_net_line_opacity.setSingleStep(0.1) self.doubleSpinBox_tf_link_net_line_opacity.setProperty("value", 0.5) self.doubleSpinBox_tf_link_net_line_opacity.setObjectName("doubleSpinBox_tf_link_net_line_opacity") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_line_opacity, 3, 1, 1, 1) - self.comboBox_tf_link_net_func_shape = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_func_shape.setObjectName("comboBox_tf_link_net_func_shape") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_func_shape, 1, 1, 1, 1) - self.label_13 = QtWidgets.QLabel(self.page_2) - self.label_13.setObjectName("label_13") - self.gridLayout_6.addWidget(self.label_13, 3, 2, 1, 1) - self.label_7 = QtWidgets.QLabel(self.page_2) - self.label_7.setObjectName("label_7") - self.gridLayout_6.addWidget(self.label_7, 0, 2, 1, 1) - self.label_12 = QtWidgets.QLabel(self.page_2) - self.label_12.setObjectName("label_12") - self.gridLayout_6.addWidget(self.label_12, 3, 0, 1, 1) - self.lineEdit_tf_link_net_taxa_focus_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_taxa_focus_color.setObjectName("lineEdit_tf_link_net_taxa_focus_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_focus_color, 0, 5, 1, 1) - self.label_11 = QtWidgets.QLabel(self.page_2) - self.label_11.setObjectName("label_11") - self.gridLayout_6.addWidget(self.label_11, 2, 0, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_line_opacity, 0, 5, 1, 1) + self.label_16 = QtWidgets.QLabel(self.page_2) + self.label_16.setObjectName("label_16") + self.gridLayout_7.addWidget(self.label_16, 2, 2, 1, 1) + self.comboBox_tf_link_net_font_weight = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_font_weight.setObjectName("comboBox_tf_link_net_font_weight") + self.comboBox_tf_link_net_font_weight.addItem("") + self.comboBox_tf_link_net_font_weight.addItem("") + self.comboBox_tf_link_net_font_weight.addItem("") + self.comboBox_tf_link_net_font_weight.addItem("") + self.gridLayout_7.addWidget(self.comboBox_tf_link_net_font_weight, 2, 3, 1, 1) + self.label_17 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_17.sizePolicy().hasHeightForWidth()) + self.label_17.setSizePolicy(sizePolicy) + self.label_17.setObjectName("label_17") + self.gridLayout_7.addWidget(self.label_17, 1, 4, 1, 1) + self.spinBox_tf_link_net_text_width = QtWidgets.QSpinBox(self.page_2) + self.spinBox_tf_link_net_text_width.setMaximum(9999) + self.spinBox_tf_link_net_text_width.setSingleStep(10) + self.spinBox_tf_link_net_text_width.setProperty("value", 300) + self.spinBox_tf_link_net_text_width.setObjectName("spinBox_tf_link_net_text_width") + self.gridLayout_7.addWidget(self.spinBox_tf_link_net_text_width, 2, 1, 1, 1) self.doubleSpinBox_tf_link_net_line_width = QtWidgets.QDoubleSpinBox(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.doubleSpinBox_tf_link_net_line_width.sizePolicy().hasHeightForWidth()) + self.doubleSpinBox_tf_link_net_line_width.setSizePolicy(sizePolicy) self.doubleSpinBox_tf_link_net_line_width.setDecimals(1) self.doubleSpinBox_tf_link_net_line_width.setMinimum(0.1) self.doubleSpinBox_tf_link_net_line_width.setSingleStep(1.0) self.doubleSpinBox_tf_link_net_line_width.setProperty("value", 3.0) self.doubleSpinBox_tf_link_net_line_width.setObjectName("doubleSpinBox_tf_link_net_line_width") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_line_width, 2, 1, 1, 1) - self.label_6 = QtWidgets.QLabel(self.page_2) - self.label_6.setObjectName("label_6") - self.gridLayout_6.addWidget(self.label_6, 1, 0, 1, 1) - self.label_17 = QtWidgets.QLabel(self.page_2) - self.label_17.setObjectName("label_17") - self.gridLayout_6.addWidget(self.label_17, 4, 0, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_line_width, 0, 1, 1, 1) self.comboBox_tf_link_net_label_position = QtWidgets.QComboBox(self.page_2) self.comboBox_tf_link_net_label_position.setObjectName("comboBox_tf_link_net_label_position") self.comboBox_tf_link_net_label_position.addItem("") @@ -169,70 +245,34 @@ def setupUi(self, Settings): self.comboBox_tf_link_net_label_position.addItem("") self.comboBox_tf_link_net_label_position.addItem("") self.comboBox_tf_link_net_label_position.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_label_position, 4, 1, 1, 1) - self.label_18 = QtWidgets.QLabel(self.page_2) - self.label_18.setObjectName("label_18") - self.gridLayout_6.addWidget(self.label_18, 4, 2, 1, 1) - self.spinBox_tf_link_net_text_width = QtWidgets.QSpinBox(self.page_2) - self.spinBox_tf_link_net_text_width.setMaximum(9999) - self.spinBox_tf_link_net_text_width.setSingleStep(10) - self.spinBox_tf_link_net_text_width.setProperty("value", 300) - self.spinBox_tf_link_net_text_width.setObjectName("spinBox_tf_link_net_text_width") - self.gridLayout_6.addWidget(self.spinBox_tf_link_net_text_width, 4, 3, 1, 1) + self.gridLayout_7.addWidget(self.comboBox_tf_link_net_label_position, 1, 5, 1, 1) self.label_19 = QtWidgets.QLabel(self.page_2) self.label_19.setObjectName("label_19") - self.gridLayout_6.addWidget(self.label_19, 4, 4, 1, 1) + self.gridLayout_7.addWidget(self.label_19, 1, 2, 1, 1) self.doubleSpinBox_tf_link_net_gravity = QtWidgets.QDoubleSpinBox(self.page_2) self.doubleSpinBox_tf_link_net_gravity.setMaximum(1.0) self.doubleSpinBox_tf_link_net_gravity.setSingleStep(0.1) self.doubleSpinBox_tf_link_net_gravity.setProperty("value", 0.2) self.doubleSpinBox_tf_link_net_gravity.setObjectName("doubleSpinBox_tf_link_net_gravity") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_gravity, 4, 5, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_6, 2, 3, 1, 1) - self.gridLayout_8 = QtWidgets.QGridLayout() - self.gridLayout_8.setObjectName("gridLayout_8") - self.label_2 = QtWidgets.QLabel(self.page_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) - self.label_2.setSizePolicy(sizePolicy) - self.label_2.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_2.setObjectName("label_2") - self.gridLayout_8.addWidget(self.label_2, 0, 0, 1, 1) - self.comboBox_heatmap_linkage_method = QtWidgets.QComboBox(self.page_2) - self.comboBox_heatmap_linkage_method.setObjectName("comboBox_heatmap_linkage_method") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_method, 0, 1, 1, 1) - self.label_3 = QtWidgets.QLabel(self.page_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) - self.label_3.setSizePolicy(sizePolicy) - self.label_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_3.setObjectName("label_3") - self.gridLayout_8.addWidget(self.label_3, 0, 2, 1, 1) - self.comboBox_heatmap_linkage_metric = QtWidgets.QComboBox(self.page_2) - self.comboBox_heatmap_linkage_metric.setObjectName("comboBox_heatmap_linkage_metric") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_metric, 0, 3, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_8, 0, 3, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_gravity, 1, 3, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_7, 2, 3, 1, 1) + self.label = QtWidgets.QLabel(self.page_2) + self.label.setObjectName("label") + self.gridLayout_4.addWidget(self.label, 0, 0, 1, 1) + self.label_21 = QtWidgets.QLabel(self.page_2) + self.label_21.setObjectName("label_21") + self.gridLayout_4.addWidget(self.label_21, 1, 0, 1, 1) + self.gridLayout_5 = QtWidgets.QGridLayout() + self.gridLayout_5.setObjectName("gridLayout_5") + self.label_22 = QtWidgets.QLabel(self.page_2) + self.label_22.setObjectName("label_22") + self.gridLayout_5.addWidget(self.label_22, 0, 0, 1, 1) + self.comboBox_html_theme = QtWidgets.QComboBox(self.page_2) + self.comboBox_html_theme.setObjectName("comboBox_html_theme") + self.comboBox_html_theme.addItem("") + self.comboBox_html_theme.addItem("") + self.gridLayout_5.addWidget(self.comboBox_html_theme, 0, 1, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_5, 1, 3, 1, 1) self.toolBox.addItem(self.page_2, "") self.gridLayout.addWidget(self.toolBox, 0, 0, 1, 1) @@ -247,30 +287,33 @@ def retranslateUi(self, Settings): self.radioButton_update_stable.setText(_translate("Settings", "Stable")) self.radioButton_update_beta.setText(_translate("Settings", "Beta")) self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("Settings", "General")) + self.label_2.setText(_translate("Settings", "Linkage Method")) + self.comboBox_heatmap_linkage_method.setCurrentText(_translate("Settings", "average")) + self.comboBox_heatmap_linkage_method.setItemText(0, _translate("Settings", "average")) + self.comboBox_heatmap_linkage_method.setItemText(1, _translate("Settings", "single")) + self.comboBox_heatmap_linkage_method.setItemText(2, _translate("Settings", "complete")) + self.comboBox_heatmap_linkage_method.setItemText(3, _translate("Settings", "centroid")) + self.comboBox_heatmap_linkage_method.setItemText(4, _translate("Settings", "median")) + self.comboBox_heatmap_linkage_method.setItemText(5, _translate("Settings", "weighted")) + self.comboBox_heatmap_linkage_method.setItemText(6, _translate("Settings", "ward")) + self.label_3.setText(_translate("Settings", "Linkage Metric")) + self.comboBox_heatmap_linkage_metric.setCurrentText(_translate("Settings", "euclidean")) + self.comboBox_heatmap_linkage_metric.setItemText(0, _translate("Settings", "euclidean")) + self.comboBox_heatmap_linkage_metric.setItemText(1, _translate("Settings", "chebyshev")) + self.comboBox_heatmap_linkage_metric.setItemText(2, _translate("Settings", "cityblock")) + self.comboBox_heatmap_linkage_metric.setItemText(3, _translate("Settings", "hamming")) + self.comboBox_heatmap_linkage_metric.setItemText(4, _translate("Settings", "matching")) + self.comboBox_heatmap_linkage_metric.setItemText(5, _translate("Settings", "minkowski")) + self.comboBox_heatmap_linkage_metric.setItemText(6, _translate("Settings", "rogerstanimoto")) + self.comboBox_heatmap_linkage_metric.setItemText(7, _translate("Settings", "russellrao")) + self.comboBox_heatmap_linkage_metric.setItemText(8, _translate("Settings", "sqeuclidean")) self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network")) - self.label.setText(_translate("Settings", "HeatMap")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(0, _translate("Settings", "circle")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(1, _translate("Settings", "rect")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(2, _translate("Settings", "roundRect")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(3, _translate("Settings", "triangle")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(4, _translate("Settings", "diamond")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(5, _translate("Settings", "pin")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(6, _translate("Settings", "arrow")) + self.label_20.setText(_translate("Settings", "Network Global")) self.label_5.setText(_translate("Settings", "Taxa Shape")) - self.comboBox_tf_link_net_font_weight.setItemText(0, _translate("Settings", "bold")) - self.comboBox_tf_link_net_font_weight.setItemText(1, _translate("Settings", "normal")) - self.comboBox_tf_link_net_font_weight.setItemText(2, _translate("Settings", "bolder")) - self.comboBox_tf_link_net_font_weight.setItemText(3, _translate("Settings", "lighter")) - self.lineEdit_tf_link_net_taxa_color.setText(_translate("Settings", "#374E55")) - self.label_15.setText(_translate("Settings", "Repulsion")) - self.label_9.setText(_translate("Settings", "Focus Taxa Color")) - self.label_10.setText(_translate("Settings", "Func Color")) - self.label_14.setText(_translate("Settings", "Line Curve")) - self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1")) - self.lineEdit_tf_link_net_func_color.setText(_translate("Settings", "#DF8F44")) - self.label_16.setText(_translate("Settings", "Font Weight")) self.lineEdit_tf_link_net_func_focus_color.setText(_translate("Settings", "#B24745")) + self.lineEdit_tf_link_net_taxa_focus_color.setText(_translate("Settings", "#6A6599")) self.label_8.setText(_translate("Settings", "Focus Func Color")) + self.lineEdit_tf_link_net_taxa_color.setText(_translate("Settings", "#374E55")) self.comboBox_tf_link_net_func_shape.setItemText(0, _translate("Settings", "rect")) self.comboBox_tf_link_net_func_shape.setItemText(1, _translate("Settings", "circle")) self.comboBox_tf_link_net_func_shape.setItemText(2, _translate("Settings", "roundRect")) @@ -278,12 +321,31 @@ def retranslateUi(self, Settings): self.comboBox_tf_link_net_func_shape.setItemText(4, _translate("Settings", "diamond")) self.comboBox_tf_link_net_func_shape.setItemText(5, _translate("Settings", "pin")) self.comboBox_tf_link_net_func_shape.setItemText(6, _translate("Settings", "arrow")) - self.label_13.setText(_translate("Settings", "Line Color")) + self.label_9.setText(_translate("Settings", "Focus Taxa Color")) self.label_7.setText(_translate("Settings", "Taxa Color")) - self.label_12.setText(_translate("Settings", "Line opacity")) - self.lineEdit_tf_link_net_taxa_focus_color.setText(_translate("Settings", "#6A6599")) - self.label_11.setText(_translate("Settings", "Line Width")) self.label_6.setText(_translate("Settings", "Function Shape")) + self.lineEdit_tf_link_net_func_color.setText(_translate("Settings", "#DF8F44")) + self.label_10.setText(_translate("Settings", "Func Color")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(0, _translate("Settings", "circle")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(1, _translate("Settings", "rect")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(2, _translate("Settings", "roundRect")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(3, _translate("Settings", "triangle")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(4, _translate("Settings", "diamond")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(5, _translate("Settings", "pin")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(6, _translate("Settings", "arrow")) + self.label_13.setText(_translate("Settings", "Line Color")) + self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1")) + self.label_11.setText(_translate("Settings", "Line Width")) + self.label_15.setToolTip(_translate("Settings", "The larger the value the greater the repulsion")) + self.label_15.setText(_translate("Settings", "Repulsion")) + self.label_12.setText(_translate("Settings", "Line opacity")) + self.label_14.setText(_translate("Settings", "Line Curve")) + self.label_18.setText(_translate("Settings", "Text Width")) + self.label_16.setText(_translate("Settings", "Font Weight")) + self.comboBox_tf_link_net_font_weight.setItemText(0, _translate("Settings", "bold")) + self.comboBox_tf_link_net_font_weight.setItemText(1, _translate("Settings", "normal")) + self.comboBox_tf_link_net_font_weight.setItemText(2, _translate("Settings", "bolder")) + self.comboBox_tf_link_net_font_weight.setItemText(3, _translate("Settings", "lighter")) self.label_17.setText(_translate("Settings", "Label Postion")) self.comboBox_tf_link_net_label_position.setItemText(0, _translate("Settings", "bottom")) self.comboBox_tf_link_net_label_position.setItemText(1, _translate("Settings", "top")) @@ -292,26 +354,11 @@ def retranslateUi(self, Settings): self.comboBox_tf_link_net_label_position.setItemText(4, _translate("Settings", "inside")) self.comboBox_tf_link_net_label_position.setItemText(5, _translate("Settings", "insideLeft")) self.comboBox_tf_link_net_label_position.setItemText(6, _translate("Settings", "insideRight")) - self.label_18.setText(_translate("Settings", "Text Width")) + self.label_19.setToolTip(_translate("Settings", "The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre.")) self.label_19.setText(_translate("Settings", "Gravity")) - self.label_2.setText(_translate("Settings", "Linkage Method")) - self.comboBox_heatmap_linkage_method.setCurrentText(_translate("Settings", "average")) - self.comboBox_heatmap_linkage_method.setItemText(0, _translate("Settings", "average")) - self.comboBox_heatmap_linkage_method.setItemText(1, _translate("Settings", "single")) - self.comboBox_heatmap_linkage_method.setItemText(2, _translate("Settings", "complete")) - self.comboBox_heatmap_linkage_method.setItemText(3, _translate("Settings", "centroid")) - self.comboBox_heatmap_linkage_method.setItemText(4, _translate("Settings", "median")) - self.comboBox_heatmap_linkage_method.setItemText(5, _translate("Settings", "weighted")) - self.comboBox_heatmap_linkage_method.setItemText(6, _translate("Settings", "ward")) - self.label_3.setText(_translate("Settings", "Linkage Metric")) - self.comboBox_heatmap_linkage_metric.setCurrentText(_translate("Settings", "euclidean")) - self.comboBox_heatmap_linkage_metric.setItemText(0, _translate("Settings", "euclidean")) - self.comboBox_heatmap_linkage_metric.setItemText(1, _translate("Settings", "chebyshev")) - self.comboBox_heatmap_linkage_metric.setItemText(2, _translate("Settings", "cityblock")) - self.comboBox_heatmap_linkage_metric.setItemText(3, _translate("Settings", "hamming")) - self.comboBox_heatmap_linkage_metric.setItemText(4, _translate("Settings", "matching")) - self.comboBox_heatmap_linkage_metric.setItemText(5, _translate("Settings", "minkowski")) - self.comboBox_heatmap_linkage_metric.setItemText(6, _translate("Settings", "rogerstanimoto")) - self.comboBox_heatmap_linkage_metric.setItemText(7, _translate("Settings", "russellrao")) - self.comboBox_heatmap_linkage_metric.setItemText(8, _translate("Settings", "sqeuclidean")) + self.label.setText(_translate("Settings", "HeatMap")) + self.label_21.setText(_translate("Settings", "HTML Global")) + self.label_22.setText(_translate("Settings", "Theme")) + self.comboBox_html_theme.setItemText(0, _translate("Settings", "white")) + self.comboBox_html_theme.setItemText(1, _translate("Settings", "dark")) self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Settings", "Others")) diff --git a/utils/version.py b/utils/version.py index fc032ef..7e16596 100644 --- a/utils/version.py +++ b/utils/version.py @@ -1,2 +1,2 @@ -__version__ = '1.107.5' +__version__ = '1.107.6' API_version = '1' \ No newline at end of file From a2c1b7c6375a6b4276698398f6d06650a7ce4e41 Mon Sep 17 00:00:00 2001 From: Qing Date: Mon, 24 Jun 2024 23:55:30 -0400 Subject: [PATCH 2/4] Changed the method of summing peptiede intensity to protein intensity, changed the method "razor" to same as MaxQuant, and added a new method "rank". --- Docs/ChangeLog.md | 5 + Docs/MetaX_Cookbook.md | 13 ++- utils/AnalyzerUtils/SumProteinIntensity.py | 113 ++++++++++++++++++--- utils/GUI.py | 8 +- utils/MetaX_GUI/MainWindow.ui | 11 +- utils/MetaX_GUI/Ui_MainWindow.py | 10 +- utils/version.py | 2 +- 7 files changed, 131 insertions(+), 31 deletions(-) diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md index 90e020b..827ef8e 100644 --- a/Docs/ChangeLog.md +++ b/Docs/ChangeLog.md @@ -1,3 +1,8 @@ +# Version: 1.107.7 +## Date: 2024-06-24 +### Changes: +- Change: Changed the method of summing peptiede intensity to protein intensity, changed the method "razor" to same as MaxQuant, and added a new method "rank". + # Version: 1.107.6 ## Date: 2024-06-19 ### Changes: diff --git a/Docs/MetaX_Cookbook.md b/Docs/MetaX_Cookbook.md index 3ef6f44..a022903 100644 --- a/Docs/MetaX_Cookbook.md +++ b/Docs/MetaX_Cookbook.md @@ -227,9 +227,16 @@ The Data Overview provides basic information about your data, such as the number Click **Create Proteins Intensity Table** to sum peptides to proteins if the Protein column is in the original table. -- **Occam's Razor and Anti-Razor:** Methods available for inferring shared peptides. - 1. Build the rank of proteins. - 2. Choose the protein with a higher rank for the shared peptide. +- **Occam's Razor**, **Anti-Razor** and **Rank:** Methods available for inferring shared peptides. + - Razor: + 1. Build a minimal set of proteins to cover all peptides (Set Cover Problem). + 2. For each peptide, choose the protein which has most peptides (if multiple proteins have the same number of peptides, share intensity to them). + - Anti-Razor: + - All proteins are shared the intensity of each peptide. + - Rank: + 1. Build the rank of proteins. + 2. Choose the protein with a higher rank for the shared peptide. + - **Methods to Build Protein Rank:** - unique_counts: Use the counts of proteins inferred by unique peptides. diff --git a/utils/AnalyzerUtils/SumProteinIntensity.py b/utils/AnalyzerUtils/SumProteinIntensity.py index db35c6b..1d29621 100644 --- a/utils/AnalyzerUtils/SumProteinIntensity.py +++ b/utils/AnalyzerUtils/SumProteinIntensity.py @@ -1,25 +1,29 @@ # This file is used to sum the protein intensity for each sample -# Method: razor or anti-razor +# Method: razor, anti-razor or rank # By sample: True or False # Output: a dataframe with protein as index and sample as columns ############################################## # USAGE: # from utils.AnalyzerUtils.SumProteinIntensity import SumProteinIntensity # out = SumProteinIntensity(sw) -# df1 = out.sum_protein_intensity(method='razor', by_sample=False, rank_method='count') -# df2 = out.sum_protein_intensity(method='razor', by_sample=False, rank_method='shared') -# df3 = out.sum_protein_intensity(method='razor', by_sample=False, rank_method='unique') +# df0 = out.sum_protein_intensity(method='razor') +# df1 = out.sum_protein_intensity(method='rank', by_sample=False, rank_method='all_counts') +# df2 = out.sum_protein_intensity(method='rank', by_sample=False, rank_method='shared_intensity') +# df3 = out.sum_protein_intensity(method='rank', by_sample=False, rank_method='unique_counts') # df4 = out.sum_protein_intensity(method='anti-razor') ############################################## +from collections import defaultdict import pandas as pd +from tqdm import tqdm + class SumProteinIntensity: def __init__(self, taxa_func_analyzer): self.tfa = taxa_func_analyzer self.res_intensity_dict = {} #store all sample to output self.rank_dict = {} #store the rank of protein intensity for each sample temporarily - self.rank_method = None + self.rank_method = None # only used for rank method self.extract_col_name = [self.tfa.peptide_col_name, self.tfa.protein_col_name] + self.tfa.sample_list self.df = self.tfa.original_df.loc[:,self.extract_col_name] self._init_dicts() @@ -27,14 +31,14 @@ def __init__(self, taxa_func_analyzer): def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='unique_counts'): - if method not in ['razor', 'anti-razor']: - raise ValueError('Method must in ["razor", "anti-razor"]') + if method not in ['razor', 'anti-razor', 'rank']: + raise ValueError('Method must in ["razor", "anti-razor", "rank"]') if rank_method not in ['shared_intensity', 'all_counts', 'unique_counts', 'unique_intensity']: raise ValueError('Rank method must in ["shared_intensity", "all_counts", "unique_counts", "unique_intensity"]') self.rank_method = rank_method - if method == 'razor': + if method == 'rank': print(f"\n-------------Start to sum protein intensity using method: [{method}] by_sample: [{by_sample}] rank_method: [{rank_method}]-------------") # make a dict to count the intensity of each protein, intensity sahred by peptides will be divided by the number of peptides if by_sample: @@ -42,7 +46,7 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un # update the dict for each sample print(f'Creating protein rank dict for [{sample}] by shared intensity', end='\r') self._update_protein_rank_dict(sample_name = sample, rank_method = rank_method) - self._sum_protein_razor(sample, by_sample) + self._sum_protein_rank(sample, by_sample) else: # without sample # only need to create the dict once @@ -51,8 +55,12 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un self._update_protein_rank_dict(sample_name = None, rank_method = rank_method) for sample in self.tfa.sample_list: - self._sum_protein_razor(sample, by_sample) - + self._sum_protein_rank(sample, by_sample) + elif method == 'razor': + print('start to sum protein intensity using method: [razor]') + # use Set Cover Problem to get the protein list, then sum the intensity + pep_to_protein = self._create_pep_to_protein_razor() + self._sum_protein_razor(pep_to_protein) elif method == 'anti-razor': print(f"\n-------------Start to sum protein intensity using method: [{method}] by_sample: [True] rank_method: [Shared]-------------") @@ -77,6 +85,82 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un return res_df + def _create_pep_to_protein_razor(self) -> dict: + """ + Create a dictionary mapping peptides to proteins based on a minimum protein set. + + Returns: + dict: A dictionary mapping peptides to proteins. + key: peptide + value: a list of proteins + """ + # crate a function to find the minimum protein set + def find_minimum_protein_set(peptides, protein_to_peptides): + print('Start to create protein dict using "Set Cover Problem"') + peptides_to_cover = set(peptides) + selected_proteins = set() + + with tqdm(total=len(peptides_to_cover), desc="Covering peptides") as pbar: + while peptides_to_cover: + best_protein = None + peptides_covered_by_best = set() + for protein, covered_peptides in protein_to_peptides.items(): + covered = peptides_to_cover & covered_peptides + if len(covered) > len(peptides_covered_by_best): + best_protein = protein + peptides_covered_by_best = covered + + if not best_protein: + break + + selected_proteins.add(best_protein) + peptides_to_cover -= peptides_covered_by_best + pbar.update(len(peptides_covered_by_best)) + + return selected_proteins + + df = self.df.loc[:, [self.tfa.peptide_col_name, self.tfa.protein_col_name]] + # Create a dictionary mapping proteins to peptides + protein_to_peptides = defaultdict(set) + peptides = set(df[self.tfa.peptide_col_name]) + + for _, row in tqdm(df.iterrows(), total=df.shape[0], desc="Creating protein to peptides mapping"): + sequence = row[self.tfa.peptide_col_name] + proteins = row[self.tfa.protein_col_name].split(';') + for protein in proteins: + protein_to_peptides[protein].add(sequence) + + mini_protein_set = find_minimum_protein_set(peptides, protein_to_peptides) + + # remove the proteins not in the mini_protein_set from the protein_to_peptides + filtered_protein_to_peptides = {protein: protein_to_peptides[protein] for protein in mini_protein_set} + # Assign each peptide to the protein that contains it with the highest peptide count + print('Assigning peptides to proteins') + peptide_to_protein = defaultdict(list) + for peptide in tqdm(peptides, desc="Assigning peptides to proteins"): + possible_proteins = [protein for protein, peps in filtered_protein_to_peptides.items() if peptide in peps] + if possible_proteins: + # 找到包含该肽最多的蛋白质 + max_protein_count = max(len(filtered_protein_to_peptides[protein]) for protein in possible_proteins) + best_proteins = [protein for protein in possible_proteins if len(filtered_protein_to_peptides[protein]) == max_protein_count] + peptide_to_protein[peptide].extend(best_proteins) + + return peptide_to_protein + + def _sum_protein_razor(self, peptide_to_protein: dict): + + for sample in tqdm(self.tfa.sample_list): + print(f'Assigning protein intensity for [{sample}]') + df = self.df.loc[:,[ self.tfa.peptide_col_name, sample]] + # create a dict to store the intensity of each peptide + df.set_index(self.tfa.peptide_col_name, inplace=True) + peptide_intensity_dict = df.to_dict()[sample] + for peptide, proteins in peptide_to_protein.items(): + intensity = peptide_intensity_dict[peptide] + self._update_output_dict(proteins, sample, intensity) + + + def _init_dicts(self): for sample in self.tfa.sample_list: self.res_intensity_dict[sample] = {} @@ -147,7 +231,7 @@ def _update_output_dict(self, protein_list: list, sample_name:str, intensity:flo self.res_intensity_dict[sample_name][protein] = intensity - def _sum_protein_razor(self, sample_name:str, by_sample=False): + def _sum_protein_rank(self, sample_name:str, by_sample=False): # print in one line print(f'Asigning protein intensity for [{sample_name}]', end='\r') df = self.df.loc[:,[ self.tfa.protein_col_name, sample_name]] @@ -180,7 +264,4 @@ def _sum_protein_anti_razor(self, sample_name:str): for row in df.itertuples(): proteins = row[1].split(';') intensity = row[2] - self._update_output_dict(proteins, sample_name, intensity) - - - + self._update_output_dict(proteins, sample_name, intensity) \ No newline at end of file diff --git a/utils/GUI.py b/utils/GUI.py index 6be1fd1..ae97625 100644 --- a/utils/GUI.py +++ b/utils/GUI.py @@ -831,8 +831,8 @@ def change_theme(self, theme, silent=False): def change_event_checkBox_create_protein_table(self): if self.checkBox_create_protein_table.isChecked(): - self.checkBox_infrence_protein_by_sample.setEnabled(True) - self.comboBox_protein_ranking_method.setEnabled(True) + # self.checkBox_infrence_protein_by_sample.setEnabled(True) + # self.comboBox_protein_ranking_method.setEnabled(True) self.comboBox_method_of_protein_inference.setEnabled(True) else: self.comboBox_method_of_protein_inference.setEnabled(False) @@ -840,12 +840,12 @@ def change_event_checkBox_create_protein_table(self): self.comboBox_protein_ranking_method.setEnabled(False) def update_method_of_protein_inference(self): - if self.comboBox_method_of_protein_inference.currentText() == "anti-razor": + if self.comboBox_method_of_protein_inference.currentText() in ["razor", "anti-razor"]: # set checked self.checkBox_infrence_protein_by_sample.setChecked(True) self.checkBox_infrence_protein_by_sample.setEnabled(False) self.comboBox_protein_ranking_method.setEnabled(False) - else: + else: # method is ["rank"] self.checkBox_infrence_protein_by_sample.setEnabled(True) self.comboBox_protein_ranking_method.setEnabled(True) self.checkBox_infrence_protein_by_sample.setChecked(False) diff --git a/utils/MetaX_GUI/MainWindow.ui b/utils/MetaX_GUI/MainWindow.ui index 2377049..1323d1f 100644 --- a/utils/MetaX_GUI/MainWindow.ui +++ b/utils/MetaX_GUI/MainWindow.ui @@ -46,7 +46,7 @@ Qt::LeftToRight - 6 + 2 @@ -798,6 +798,11 @@ anti-razor + + + rank + + @@ -1315,7 +1320,7 @@ - 1 + 0 @@ -8026,7 +8031,7 @@ 0 0 1059 - 21 + 23 diff --git a/utils/MetaX_GUI/Ui_MainWindow.py b/utils/MetaX_GUI/Ui_MainWindow.py index 8f51c6b..af9d508 100644 --- a/utils/MetaX_GUI/Ui_MainWindow.py +++ b/utils/MetaX_GUI/Ui_MainWindow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'c:\Users\Qing\OneDrive - University of Ottawa\code\TaxaFunc\MetaX\utils\MetaX_GUI\MainWindow.ui' +# Form implementation generated from reading ui file 'c:\Users\max\OneDrive - University of Ottawa\code\TaxaFunc\MetaX\utils\MetaX_GUI\MainWindow.ui' # # Created by: PyQt5 UI code generator 5.15.9 # @@ -438,6 +438,7 @@ def setupUi(self, metaX_main): self.comboBox_method_of_protein_inference.setObjectName("comboBox_method_of_protein_inference") self.comboBox_method_of_protein_inference.addItem("") self.comboBox_method_of_protein_inference.addItem("") + self.comboBox_method_of_protein_inference.addItem("") self.gridLayout_37.addWidget(self.comboBox_method_of_protein_inference, 0, 2, 1, 1) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") @@ -4208,7 +4209,7 @@ def setupUi(self, metaX_main): self.statusbar.setObjectName("statusbar") metaX_main.setStatusBar(self.statusbar) self.menuBar = QtWidgets.QMenuBar(metaX_main) - self.menuBar.setGeometry(QtCore.QRect(0, 0, 1059, 21)) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 1059, 23)) self.menuBar.setObjectName("menuBar") self.menuTools = QtWidgets.QMenu(self.menuBar) self.menuTools.setObjectName("menuTools") @@ -4268,9 +4269,9 @@ def setupUi(self, metaX_main): self.retranslateUi(metaX_main) self.stackedWidget.setCurrentIndex(0) - self.tabWidget_TaxaFuncAnalyzer.setCurrentIndex(6) + self.tabWidget_TaxaFuncAnalyzer.setCurrentIndex(2) self.toolBox_2.setCurrentIndex(0) - self.tabWidget_4.setCurrentIndex(1) + self.tabWidget_4.setCurrentIndex(0) self.tabWidget_3.setCurrentIndex(0) self.tabWidget.setCurrentIndex(1) self.tabWidget_2.setCurrentIndex(1) @@ -4369,6 +4370,7 @@ def retranslateUi(self, metaX_main): self.checkBox_infrence_protein_by_sample.setText(_translate("metaX_main", "Inference by each Sample")) self.comboBox_method_of_protein_inference.setItemText(0, _translate("metaX_main", "razor")) self.comboBox_method_of_protein_inference.setItemText(1, _translate("metaX_main", "anti-razor")) + self.comboBox_method_of_protein_inference.setItemText(2, _translate("metaX_main", "rank")) self.label_136.setText(_translate("metaX_main", "Protein Ranking Method")) self.comboBox_protein_ranking_method.setItemText(0, _translate("metaX_main", "unique_counts")) self.comboBox_protein_ranking_method.setItemText(1, _translate("metaX_main", "all_counts")) diff --git a/utils/version.py b/utils/version.py index 7e16596..7663ef1 100644 --- a/utils/version.py +++ b/utils/version.py @@ -1,2 +1,2 @@ -__version__ = '1.107.6' +__version__ = '1.107.7' API_version = '1' \ No newline at end of file From 9597752ce81e781509bb532a81b40509dc1e19eb Mon Sep 17 00:00:00 2001 From: Qing <44231502+byemaxx@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:01:12 -0400 Subject: [PATCH 3/4] 1.Use the heap as the default data structure to apply razor method to sum peptide intensity to protein intensity. 2.Changed the update message to a dialog window to show the update message to avoid the update message is too long to show in the message box. --- Docs/ChangeLog.md | 7 + Docs/MetaX_Cookbook.md | 2 +- utils/AnalyzerUtils/SumProteinIntensity.py | 86 +++- utils/GUI.py | 20 +- utils/MetaX_GUI/Setting.ui | 537 +++++++++++---------- utils/MetaX_GUI/Settings.py | 24 +- utils/MetaX_GUI/Ui_Setting.py | 360 +++++++------- utils/TaxaFuncAnalyzer.py | 6 +- utils/metax_updater.py | 63 ++- utils/version.py | 2 +- 10 files changed, 642 insertions(+), 465 deletions(-) diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md index 827ef8e..ca4ca32 100644 --- a/Docs/ChangeLog.md +++ b/Docs/ChangeLog.md @@ -1,3 +1,10 @@ +# Version: 1.107.8 +## Date: 2024-06-26 +### Changes: +- Change: +- 1.Use the heap as the default data structure to apply razor method to sum peptide intensity to protein intensity. +- 2.Changed the update message to a dialog window to show the update message to avoid the update message is too long to show in the message box. + # Version: 1.107.7 ## Date: 2024-06-24 ### Changes: diff --git a/Docs/MetaX_Cookbook.md b/Docs/MetaX_Cookbook.md index a022903..eb1daf7 100644 --- a/Docs/MetaX_Cookbook.md +++ b/Docs/MetaX_Cookbook.md @@ -229,7 +229,7 @@ Click **Create Proteins Intensity Table** to sum peptides to proteins if the Pro - **Occam's Razor**, **Anti-Razor** and **Rank:** Methods available for inferring shared peptides. - Razor: - 1. Build a minimal set of proteins to cover all peptides (Set Cover Problem). + 1. Build a minimal set of proteins to cover all peptides. 2. For each peptide, choose the protein which has most peptides (if multiple proteins have the same number of peptides, share intensity to them). - Anti-Razor: - All proteins are shared the intensity of each peptide. diff --git a/utils/AnalyzerUtils/SumProteinIntensity.py b/utils/AnalyzerUtils/SumProteinIntensity.py index 1d29621..55bd907 100644 --- a/utils/AnalyzerUtils/SumProteinIntensity.py +++ b/utils/AnalyzerUtils/SumProteinIntensity.py @@ -21,15 +21,16 @@ class SumProteinIntensity: def __init__(self, taxa_func_analyzer): self.tfa = taxa_func_analyzer - self.res_intensity_dict = {} #store all sample to output - self.rank_dict = {} #store the rank of protein intensity for each sample temporarily - self.rank_method = None # only used for rank method + self.res_intensity_dict = {} # store all sample to output + self.rank_dict = {} # store the rank of protein intensity for each sample temporarily + self.rank_method = None # only used for rank method self.extract_col_name = [self.tfa.peptide_col_name, self.tfa.protein_col_name] + self.tfa.sample_list - self.df = self.tfa.original_df.loc[:,self.extract_col_name] + self.df = self.tfa.original_df.loc[:, self.extract_col_name] self._init_dicts() + self.greedy_method = None # only used for razor method - def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='unique_counts'): + def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='unique_counts', greedy_method='heap'): if method not in ['razor', 'anti-razor', 'rank']: raise ValueError('Method must in ["razor", "anti-razor", "rank"]') @@ -37,6 +38,7 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un raise ValueError('Rank method must in ["shared_intensity", "all_counts", "unique_counts", "unique_intensity"]') self.rank_method = rank_method + self.greedy_method = greedy_method if method == 'rank': print(f"\n-------------Start to sum protein intensity using method: [{method}] by_sample: [{by_sample}] rank_method: [{rank_method}]-------------") @@ -84,27 +86,20 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un return res_df - - def _create_pep_to_protein_razor(self) -> dict: - """ - Create a dictionary mapping peptides to proteins based on a minimum protein set. - - Returns: - dict: A dictionary mapping peptides to proteins. - key: peptide - value: a list of proteins - """ - # crate a function to find the minimum protein set - def find_minimum_protein_set(peptides, protein_to_peptides): - print('Start to create protein dict using "Set Cover Problem"') - peptides_to_cover = set(peptides) - selected_proteins = set() - + # razor method + def find_minimum_protein_set(self, peptides, protein_to_peptides): + protein_to_peptides_copy = protein_to_peptides.copy() + peptides_to_cover = set(peptides) + selected_proteins = set() + method = self.greedy_method + + if method == 'greedy': + print('Start creating protein dict for "Set Cover Problem" with Greedy Approximation Algorithm') with tqdm(total=len(peptides_to_cover), desc="Covering peptides") as pbar: while peptides_to_cover: best_protein = None peptides_covered_by_best = set() - for protein, covered_peptides in protein_to_peptides.items(): + for protein, covered_peptides in protein_to_peptides_copy.items(): covered = peptides_to_cover & covered_peptides if len(covered) > len(peptides_covered_by_best): best_protein = protein @@ -115,9 +110,52 @@ def find_minimum_protein_set(peptides, protein_to_peptides): selected_proteins.add(best_protein) peptides_to_cover -= peptides_covered_by_best + protein_to_peptides_copy.pop(best_protein) # remove the protein from the dict to speed up the process pbar.update(len(peptides_covered_by_best)) + elif method == 'heap': + import heapq + print('Start creating protein dict for "Set Cover Problem" with Heap Optimization of Greedy Approximation Algorithm') + protein_coverage = {protein: covered_peptides & peptides_to_cover + for protein, covered_peptides in protein_to_peptides_copy.items()} + protein_heap = [(-len(covered), protein) for protein, covered in protein_coverage.items()] + heapq.heapify(protein_heap) + + with tqdm(total=len(peptides_to_cover), desc="Covering peptides") as pbar: + while peptides_to_cover: + while protein_heap: + max_covered, best_protein = heapq.heappop(protein_heap) + if best_protein in protein_coverage: + peptides_covered_by_best = protein_coverage.pop(best_protein) + break + + if not best_protein or not peptides_covered_by_best: + break - return selected_proteins + selected_proteins.add(best_protein) + peptides_to_cover -= peptides_covered_by_best + pbar.update(len(peptides_covered_by_best)) + + # update other proteins' coverage + for protein in list(protein_coverage.keys()): + if protein_coverage[protein] & peptides_covered_by_best: + protein_coverage[protein] -= peptides_covered_by_best + heapq.heappush(protein_heap, (-len(protein_coverage[protein]), protein)) + if not protein_coverage[protein]: + del protein_coverage[protein] + else: + raise ValueError(f"Invalid greedy method: {method}. Must be ['greedy' or 'heap']") + + return selected_proteins + + def _create_pep_to_protein_razor(self) -> dict: + """ + Create a dictionary mapping peptides to proteins based on a minimum protein set. + + Returns: + dict: A dictionary mapping peptides to proteins. + key: peptide + value: a list of proteins + """ df = self.df.loc[:, [self.tfa.peptide_col_name, self.tfa.protein_col_name]] # Create a dictionary mapping proteins to peptides @@ -130,7 +168,7 @@ def find_minimum_protein_set(peptides, protein_to_peptides): for protein in proteins: protein_to_peptides[protein].add(sequence) - mini_protein_set = find_minimum_protein_set(peptides, protein_to_peptides) + mini_protein_set = self.find_minimum_protein_set(peptides, protein_to_peptides) # remove the proteins not in the mini_protein_set from the protein_to_peptides filtered_protein_to_peptides = {protein: protein_to_peptides[protein] for protein in mini_protein_set} diff --git a/utils/GUI.py b/utils/GUI.py index ae97625..77a1dfc 100644 --- a/utils/GUI.py +++ b/utils/GUI.py @@ -650,13 +650,21 @@ def show_settings_window(self): self.settings_dialog.setModal(False) layout = QVBoxLayout(self.settings_dialog) self.settings_dialog.resize(900, 600) - - settings_widget = SettingsWidget(self.settings_dialog, self.update_branch, self.auto_check_update) + # General settings + settings_widget = SettingsWidget( + parent=self.settings_dialog, + update_branch=self.update_branch, + auto_check_update=self.auto_check_update, + QSettings=self.settings, + ) settings_widget.update_mode_changed.connect(self.on_update_mode_changed) settings_widget.auto_check_update_changed.connect(self.on_auto_check_update_changed) + # plotting parameters settings_widget.heatmap_params_dict_changed.connect(self.on_heatmap_params_changed) settings_widget.tf_link_net_params_dict_changed.connect(self.on_tf_link_net_params_changed) settings_widget.html_theme_changed.connect(self.on_html_theme_changed) + # Other settings + settings_widget.protein_infer_method_changed.connect(self.on_protein_infer_method_changed) layout.addWidget(settings_widget) self.settings_dialog.setLayout(layout) @@ -686,6 +694,11 @@ def on_tf_link_net_params_changed(self, params_dict): def on_html_theme_changed(self, theme): self.html_theme = theme print(f"HTML theme changed to: {theme}") + + def on_protein_infer_method_changed(self, method): + #save to settings + self.settings.setValue("protein_infer_greedy_mode", method) + print(f"Protein infering razor mode changed to: {method}") ############### basic function End ############### @@ -2478,7 +2491,8 @@ def set_multi_table(self, restore_taxafunc=False, saved_obj=None): sum_protein_params = { 'method': self.comboBox_method_of_protein_inference.currentText(), 'by_sample': self.checkBox_infrence_protein_by_sample.isChecked(), - 'rank_method' :self.comboBox_protein_ranking_method.currentText() + 'rank_method' :self.comboBox_protein_ranking_method.currentText(), + 'greedy_method': self.settings.value('protein_infer_greedy_mode', 'heap') } diff --git a/utils/MetaX_GUI/Setting.ui b/utils/MetaX_GUI/Setting.ui index 6f5ccba..5221958 100644 --- a/utils/MetaX_GUI/Setting.ui +++ b/utils/MetaX_GUI/Setting.ui @@ -17,7 +17,7 @@ - 1 + 2 @@ -25,7 +25,7 @@ 0 0 748 - 367 + 340 @@ -71,161 +71,286 @@ 0 0 748 - 367 + 340 - Others + Plotting - - + + + + HTML Global + + + + + - + 0 0 - - Qt::LeftToRight + + Line Width + + + + + + + 1 + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.000000000000000 + + + + + + + The larger the value the greater the repulsion - Linkage Method + Repulsion - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + Line opacity - - - - average + + + + + 0 + 0 + + + Line Curve + + + + + + + Text Width + + + + + + + 100000 + + + 10 + + + 500 + + + + + + + 1 + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + + Font Weight + + + + + - average - - - - - single - - - - - complete - - - - - centroid + bold - median + normal - weighted + bolder - ward + lighter - - + + - + 0 0 - Linkage Metric + Label Postion - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 9999 + + + 10 + + + 300 - - - - euclidean + + + + + 0 + 0 + + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + 3.000000000000000 + + + + + - euclidean + bottom - chebyshev + top - cityblock + right - hamming + left - matching + inside - minkowski + insideLeft - rogerstanimoto + insideRight + + + + + + The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre. + + + Gravity + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.200000000000000 + + + + + + + + + + + Theme + + + + + - russellrao + white - sqeuclidean + dark - - - - Taxa-Functions Link Network - - - - - - - Network Global - - - - + @@ -391,283 +516,197 @@ - - + + + + Taxa-Functions Link Network + + + + + - - - - 0 - 0 - - - - Line Width - - - - - - - 1 - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.000000000000000 - - - - - - - The larger the value the greater the repulsion - - - Repulsion - - - - - - - Line opacity - - - - - + 0 0 - - Line Curve + + Qt::LeftToRight - - - - - Text Width - - - - - - - 100000 - - - 10 - - - 500 - - - - - - - 1 - - - 1.000000000000000 - - - 0.100000000000000 + Linkage Method - - 0.500000000000000 + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - Font Weight + + + + average - - - - - bold + average - normal + single - bolder + complete - lighter + centroid + + + + + median + + + + + weighted + + + + + ward - - + + - + 0 0 - Label Postion - - - - - - - 9999 - - - 10 + Linkage Metric - - 300 + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - 0 - 0 - - - - 1 - - - 0.100000000000000 - - - 1.000000000000000 - - - 3.000000000000000 + + + + euclidean - - - - - bottom + euclidean - top + chebyshev - right + cityblock - left + hamming - inside + matching - insideLeft + minkowski - insideRight + rogerstanimoto + + + + + russellrao + + + + + sqeuclidean - - - - - - The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre. - - - Gravity - - - - - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.200000000000000 - - + + + + Network Global + + + + HeatMap + + + + + Others + + - + - HTML Global + Protein Infer - - - - - - Theme - - - + + - + - white + fast - dark + normal + + + + Greedy Mode in Razor Method + + + diff --git a/utils/MetaX_GUI/Settings.py b/utils/MetaX_GUI/Settings.py index 667073d..165970c 100644 --- a/utils/MetaX_GUI/Settings.py +++ b/utils/MetaX_GUI/Settings.py @@ -8,8 +8,9 @@ class SettingsWidget(QWidget): heatmap_params_dict_changed = pyqtSignal(dict) tf_link_net_params_dict_changed = pyqtSignal(dict) html_theme_changed = pyqtSignal(str) + protein_infer_method_changed = pyqtSignal(str) - def __init__(self, parent=None, update_branch="main", auto_check_update=True): + def __init__(self, parent=None, update_branch="main", auto_check_update=True, QSettings=None): super().__init__(parent) self.update_mode = update_branch self.auto_check_update = auto_check_update @@ -20,7 +21,7 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True): self.ui = Ui_Settings() self.ui.setupUi(self) - self.init_ui(self.update_mode, self.auto_check_update) + self.init_ui(self.update_mode, self.auto_check_update, QSettings) # resize the window, 800 as default self.resize(800, 400) @@ -59,13 +60,23 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True): # HTML theme self.ui.comboBox_html_theme.currentTextChanged.connect(self.handle_html_theme_changed) + # Protein inference method + self.ui.comboBox_protein_infer_greedy_mode.currentTextChanged.connect(self.handle_protein_infer_method_changed) - def init_ui(self, update_mode, auto_check_update): + + def init_ui(self, update_mode, auto_check_update, QSettings=None): if update_mode == "main": self.ui.radioButton_update_stable.setChecked(True) elif update_mode == "dev": self.ui.radioButton_update_beta.setChecked(True) self.ui.checkBox_auto_check_update.setChecked(auto_check_update) + + if QSettings: + method = QSettings.value('protein_infer_greedy_mode', 'fast') + selected_method = 'normal' if method == 'greedy' else 'fast' + print(f"Protein inference method: {method}") + self.ui.comboBox_protein_infer_greedy_mode.setCurrentText(selected_method) + def handle_checkbox_state_changed(self): self.auto_check_update = self.ui.checkBox_auto_check_update.isChecked() @@ -118,6 +129,13 @@ def handle_html_theme_changed(self): theme = self.ui.comboBox_html_theme.currentText() self.html_theme_changed.emit(theme) + def handle_protein_infer_method_changed(self): + protein_infer_greedy_mode = self.ui.comboBox_protein_infer_greedy_mode.currentText() + method = { + 'normal': 'greedy', + 'fast': 'heap', + } + self.protein_infer_method_changed.emit(method[protein_infer_greedy_mode]) if __name__ == "__main__": import sys diff --git a/utils/MetaX_GUI/Ui_Setting.py b/utils/MetaX_GUI/Ui_Setting.py index 177380a..395abc1 100644 --- a/utils/MetaX_GUI/Ui_Setting.py +++ b/utils/MetaX_GUI/Ui_Setting.py @@ -20,7 +20,7 @@ def setupUi(self, Settings): self.toolBox = QtWidgets.QToolBox(Settings) self.toolBox.setObjectName("toolBox") self.page = QtWidgets.QWidget() - self.page.setGeometry(QtCore.QRect(0, 0, 748, 367)) + self.page.setGeometry(QtCore.QRect(0, 0, 748, 340)) self.page.setObjectName("page") self.gridLayout_3 = QtWidgets.QGridLayout(self.page) self.gridLayout_3.setObjectName("gridLayout_3") @@ -40,119 +40,13 @@ def setupUi(self, Settings): self.gridLayout_3.addLayout(self.gridLayout_2, 0, 0, 1, 1) self.toolBox.addItem(self.page, "") self.page_2 = QtWidgets.QWidget() - self.page_2.setGeometry(QtCore.QRect(0, 0, 748, 367)) + self.page_2.setGeometry(QtCore.QRect(0, 0, 748, 340)) self.page_2.setObjectName("page_2") self.gridLayout_4 = QtWidgets.QGridLayout(self.page_2) self.gridLayout_4.setObjectName("gridLayout_4") - self.gridLayout_8 = QtWidgets.QGridLayout() - self.gridLayout_8.setObjectName("gridLayout_8") - self.label_2 = QtWidgets.QLabel(self.page_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) - self.label_2.setSizePolicy(sizePolicy) - self.label_2.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_2.setObjectName("label_2") - self.gridLayout_8.addWidget(self.label_2, 0, 0, 1, 1) - self.comboBox_heatmap_linkage_method = QtWidgets.QComboBox(self.page_2) - self.comboBox_heatmap_linkage_method.setObjectName("comboBox_heatmap_linkage_method") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.comboBox_heatmap_linkage_method.addItem("") - self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_method, 0, 1, 1, 1) - self.label_3 = QtWidgets.QLabel(self.page_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) - self.label_3.setSizePolicy(sizePolicy) - self.label_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_3.setObjectName("label_3") - self.gridLayout_8.addWidget(self.label_3, 0, 2, 1, 1) - self.comboBox_heatmap_linkage_metric = QtWidgets.QComboBox(self.page_2) - self.comboBox_heatmap_linkage_metric.setObjectName("comboBox_heatmap_linkage_metric") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.comboBox_heatmap_linkage_metric.addItem("") - self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_metric, 0, 3, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_8, 0, 3, 1, 1) - self.label_4 = QtWidgets.QLabel(self.page_2) - self.label_4.setObjectName("label_4") - self.gridLayout_4.addWidget(self.label_4, 3, 0, 1, 1) - self.label_20 = QtWidgets.QLabel(self.page_2) - self.label_20.setObjectName("label_20") - self.gridLayout_4.addWidget(self.label_20, 2, 0, 1, 1) - self.gridLayout_6 = QtWidgets.QGridLayout() - self.gridLayout_6.setObjectName("gridLayout_6") - self.label_5 = QtWidgets.QLabel(self.page_2) - self.label_5.setObjectName("label_5") - self.gridLayout_6.addWidget(self.label_5, 0, 0, 1, 1) - self.lineEdit_tf_link_net_func_focus_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_func_focus_color.setObjectName("lineEdit_tf_link_net_func_focus_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_focus_color, 1, 5, 1, 1) - self.lineEdit_tf_link_net_taxa_focus_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_taxa_focus_color.setObjectName("lineEdit_tf_link_net_taxa_focus_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_focus_color, 0, 5, 1, 1) - self.label_8 = QtWidgets.QLabel(self.page_2) - self.label_8.setObjectName("label_8") - self.gridLayout_6.addWidget(self.label_8, 1, 4, 1, 1) - self.lineEdit_tf_link_net_taxa_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_taxa_color.setObjectName("lineEdit_tf_link_net_taxa_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_color, 0, 3, 1, 1) - self.comboBox_tf_link_net_func_shape = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_func_shape.setObjectName("comboBox_tf_link_net_func_shape") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_func_shape, 1, 1, 1, 1) - self.label_9 = QtWidgets.QLabel(self.page_2) - self.label_9.setObjectName("label_9") - self.gridLayout_6.addWidget(self.label_9, 0, 4, 1, 1) - self.label_7 = QtWidgets.QLabel(self.page_2) - self.label_7.setObjectName("label_7") - self.gridLayout_6.addWidget(self.label_7, 0, 2, 1, 1) - self.label_6 = QtWidgets.QLabel(self.page_2) - self.label_6.setObjectName("label_6") - self.gridLayout_6.addWidget(self.label_6, 1, 0, 1, 1) - self.lineEdit_tf_link_net_func_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_func_color.setObjectName("lineEdit_tf_link_net_func_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_color, 1, 3, 1, 1) - self.label_10 = QtWidgets.QLabel(self.page_2) - self.label_10.setObjectName("label_10") - self.gridLayout_6.addWidget(self.label_10, 1, 2, 1, 1) - self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_taxa_sahpe.setObjectName("comboBox_tf_link_net_taxa_sahpe") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_taxa_sahpe, 0, 1, 1, 1) - self.label_13 = QtWidgets.QLabel(self.page_2) - self.label_13.setObjectName("label_13") - self.gridLayout_6.addWidget(self.label_13, 2, 0, 1, 1) - self.lineEdit_tf_link_net_line_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_line_color.setObjectName("lineEdit_tf_link_net_line_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 2, 1, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_6, 3, 3, 1, 1) + self.label_21 = QtWidgets.QLabel(self.page_2) + self.label_21.setObjectName("label_21") + self.gridLayout_4.addWidget(self.label_21, 2, 0, 1, 1) self.gridLayout_7 = QtWidgets.QGridLayout() self.gridLayout_7.setObjectName("gridLayout_7") self.label_11 = QtWidgets.QLabel(self.page_2) @@ -255,13 +149,7 @@ def setupUi(self, Settings): self.doubleSpinBox_tf_link_net_gravity.setProperty("value", 0.2) self.doubleSpinBox_tf_link_net_gravity.setObjectName("doubleSpinBox_tf_link_net_gravity") self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_gravity, 1, 3, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_7, 2, 3, 1, 1) - self.label = QtWidgets.QLabel(self.page_2) - self.label.setObjectName("label") - self.gridLayout_4.addWidget(self.label, 0, 0, 1, 1) - self.label_21 = QtWidgets.QLabel(self.page_2) - self.label_21.setObjectName("label_21") - self.gridLayout_4.addWidget(self.label_21, 1, 0, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_7, 3, 3, 1, 1) self.gridLayout_5 = QtWidgets.QGridLayout() self.gridLayout_5.setObjectName("gridLayout_5") self.label_22 = QtWidgets.QLabel(self.page_2) @@ -272,12 +160,143 @@ def setupUi(self, Settings): self.comboBox_html_theme.addItem("") self.comboBox_html_theme.addItem("") self.gridLayout_5.addWidget(self.comboBox_html_theme, 0, 1, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_5, 1, 3, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_5, 2, 3, 1, 1) + self.gridLayout_6 = QtWidgets.QGridLayout() + self.gridLayout_6.setObjectName("gridLayout_6") + self.label_5 = QtWidgets.QLabel(self.page_2) + self.label_5.setObjectName("label_5") + self.gridLayout_6.addWidget(self.label_5, 0, 0, 1, 1) + self.lineEdit_tf_link_net_func_focus_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_func_focus_color.setObjectName("lineEdit_tf_link_net_func_focus_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_focus_color, 1, 5, 1, 1) + self.lineEdit_tf_link_net_taxa_focus_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_taxa_focus_color.setObjectName("lineEdit_tf_link_net_taxa_focus_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_focus_color, 0, 5, 1, 1) + self.label_8 = QtWidgets.QLabel(self.page_2) + self.label_8.setObjectName("label_8") + self.gridLayout_6.addWidget(self.label_8, 1, 4, 1, 1) + self.lineEdit_tf_link_net_taxa_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_taxa_color.setObjectName("lineEdit_tf_link_net_taxa_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_color, 0, 3, 1, 1) + self.comboBox_tf_link_net_func_shape = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_func_shape.setObjectName("comboBox_tf_link_net_func_shape") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_func_shape, 1, 1, 1, 1) + self.label_9 = QtWidgets.QLabel(self.page_2) + self.label_9.setObjectName("label_9") + self.gridLayout_6.addWidget(self.label_9, 0, 4, 1, 1) + self.label_7 = QtWidgets.QLabel(self.page_2) + self.label_7.setObjectName("label_7") + self.gridLayout_6.addWidget(self.label_7, 0, 2, 1, 1) + self.label_6 = QtWidgets.QLabel(self.page_2) + self.label_6.setObjectName("label_6") + self.gridLayout_6.addWidget(self.label_6, 1, 0, 1, 1) + self.lineEdit_tf_link_net_func_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_func_color.setObjectName("lineEdit_tf_link_net_func_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_color, 1, 3, 1, 1) + self.label_10 = QtWidgets.QLabel(self.page_2) + self.label_10.setObjectName("label_10") + self.gridLayout_6.addWidget(self.label_10, 1, 2, 1, 1) + self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_taxa_sahpe.setObjectName("comboBox_tf_link_net_taxa_sahpe") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_taxa_sahpe, 0, 1, 1, 1) + self.label_13 = QtWidgets.QLabel(self.page_2) + self.label_13.setObjectName("label_13") + self.gridLayout_6.addWidget(self.label_13, 2, 0, 1, 1) + self.lineEdit_tf_link_net_line_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_line_color.setObjectName("lineEdit_tf_link_net_line_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 2, 1, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_6, 4, 3, 1, 1) + self.label_4 = QtWidgets.QLabel(self.page_2) + self.label_4.setObjectName("label_4") + self.gridLayout_4.addWidget(self.label_4, 4, 0, 1, 1) + self.gridLayout_8 = QtWidgets.QGridLayout() + self.gridLayout_8.setObjectName("gridLayout_8") + self.label_2 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) + self.label_2.setSizePolicy(sizePolicy) + self.label_2.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_2.setObjectName("label_2") + self.gridLayout_8.addWidget(self.label_2, 0, 0, 1, 1) + self.comboBox_heatmap_linkage_method = QtWidgets.QComboBox(self.page_2) + self.comboBox_heatmap_linkage_method.setObjectName("comboBox_heatmap_linkage_method") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.comboBox_heatmap_linkage_method.addItem("") + self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_method, 0, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) + self.label_3.setSizePolicy(sizePolicy) + self.label_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_3.setObjectName("label_3") + self.gridLayout_8.addWidget(self.label_3, 0, 2, 1, 1) + self.comboBox_heatmap_linkage_metric = QtWidgets.QComboBox(self.page_2) + self.comboBox_heatmap_linkage_metric.setObjectName("comboBox_heatmap_linkage_metric") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.comboBox_heatmap_linkage_metric.addItem("") + self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_metric, 0, 3, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_8, 1, 3, 1, 1) + self.label_20 = QtWidgets.QLabel(self.page_2) + self.label_20.setObjectName("label_20") + self.gridLayout_4.addWidget(self.label_20, 3, 0, 1, 1) + self.label = QtWidgets.QLabel(self.page_2) + self.label.setObjectName("label") + self.gridLayout_4.addWidget(self.label, 1, 0, 1, 1) self.toolBox.addItem(self.page_2, "") + self.page_3 = QtWidgets.QWidget() + self.page_3.setObjectName("page_3") + self.gridLayout_11 = QtWidgets.QGridLayout(self.page_3) + self.gridLayout_11.setObjectName("gridLayout_11") + self.label_23 = QtWidgets.QLabel(self.page_3) + self.label_23.setObjectName("label_23") + self.gridLayout_11.addWidget(self.label_23, 1, 0, 1, 1) + self.gridLayout_10 = QtWidgets.QGridLayout() + self.gridLayout_10.setObjectName("gridLayout_10") + self.comboBox_protein_infer_greedy_mode = QtWidgets.QComboBox(self.page_3) + self.comboBox_protein_infer_greedy_mode.setObjectName("comboBox_protein_infer_greedy_mode") + self.comboBox_protein_infer_greedy_mode.addItem("") + self.comboBox_protein_infer_greedy_mode.addItem("") + self.gridLayout_10.addWidget(self.comboBox_protein_infer_greedy_mode, 0, 1, 1, 1) + self.label_24 = QtWidgets.QLabel(self.page_3) + self.label_24.setObjectName("label_24") + self.gridLayout_10.addWidget(self.label_24, 0, 0, 1, 1) + self.gridLayout_11.addLayout(self.gridLayout_10, 1, 1, 1, 1) + self.toolBox.addItem(self.page_3, "") self.gridLayout.addWidget(self.toolBox, 0, 0, 1, 1) self.retranslateUi(Settings) - self.toolBox.setCurrentIndex(1) + self.toolBox.setCurrentIndex(2) QtCore.QMetaObject.connectSlotsByName(Settings) def retranslateUi(self, Settings): @@ -287,28 +306,31 @@ def retranslateUi(self, Settings): self.radioButton_update_stable.setText(_translate("Settings", "Stable")) self.radioButton_update_beta.setText(_translate("Settings", "Beta")) self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("Settings", "General")) - self.label_2.setText(_translate("Settings", "Linkage Method")) - self.comboBox_heatmap_linkage_method.setCurrentText(_translate("Settings", "average")) - self.comboBox_heatmap_linkage_method.setItemText(0, _translate("Settings", "average")) - self.comboBox_heatmap_linkage_method.setItemText(1, _translate("Settings", "single")) - self.comboBox_heatmap_linkage_method.setItemText(2, _translate("Settings", "complete")) - self.comboBox_heatmap_linkage_method.setItemText(3, _translate("Settings", "centroid")) - self.comboBox_heatmap_linkage_method.setItemText(4, _translate("Settings", "median")) - self.comboBox_heatmap_linkage_method.setItemText(5, _translate("Settings", "weighted")) - self.comboBox_heatmap_linkage_method.setItemText(6, _translate("Settings", "ward")) - self.label_3.setText(_translate("Settings", "Linkage Metric")) - self.comboBox_heatmap_linkage_metric.setCurrentText(_translate("Settings", "euclidean")) - self.comboBox_heatmap_linkage_metric.setItemText(0, _translate("Settings", "euclidean")) - self.comboBox_heatmap_linkage_metric.setItemText(1, _translate("Settings", "chebyshev")) - self.comboBox_heatmap_linkage_metric.setItemText(2, _translate("Settings", "cityblock")) - self.comboBox_heatmap_linkage_metric.setItemText(3, _translate("Settings", "hamming")) - self.comboBox_heatmap_linkage_metric.setItemText(4, _translate("Settings", "matching")) - self.comboBox_heatmap_linkage_metric.setItemText(5, _translate("Settings", "minkowski")) - self.comboBox_heatmap_linkage_metric.setItemText(6, _translate("Settings", "rogerstanimoto")) - self.comboBox_heatmap_linkage_metric.setItemText(7, _translate("Settings", "russellrao")) - self.comboBox_heatmap_linkage_metric.setItemText(8, _translate("Settings", "sqeuclidean")) - self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network")) - self.label_20.setText(_translate("Settings", "Network Global")) + self.label_21.setText(_translate("Settings", "HTML Global")) + self.label_11.setText(_translate("Settings", "Line Width")) + self.label_15.setToolTip(_translate("Settings", "The larger the value the greater the repulsion")) + self.label_15.setText(_translate("Settings", "Repulsion")) + self.label_12.setText(_translate("Settings", "Line opacity")) + self.label_14.setText(_translate("Settings", "Line Curve")) + self.label_18.setText(_translate("Settings", "Text Width")) + self.label_16.setText(_translate("Settings", "Font Weight")) + self.comboBox_tf_link_net_font_weight.setItemText(0, _translate("Settings", "bold")) + self.comboBox_tf_link_net_font_weight.setItemText(1, _translate("Settings", "normal")) + self.comboBox_tf_link_net_font_weight.setItemText(2, _translate("Settings", "bolder")) + self.comboBox_tf_link_net_font_weight.setItemText(3, _translate("Settings", "lighter")) + self.label_17.setText(_translate("Settings", "Label Postion")) + self.comboBox_tf_link_net_label_position.setItemText(0, _translate("Settings", "bottom")) + self.comboBox_tf_link_net_label_position.setItemText(1, _translate("Settings", "top")) + self.comboBox_tf_link_net_label_position.setItemText(2, _translate("Settings", "right")) + self.comboBox_tf_link_net_label_position.setItemText(3, _translate("Settings", "left")) + self.comboBox_tf_link_net_label_position.setItemText(4, _translate("Settings", "inside")) + self.comboBox_tf_link_net_label_position.setItemText(5, _translate("Settings", "insideLeft")) + self.comboBox_tf_link_net_label_position.setItemText(6, _translate("Settings", "insideRight")) + self.label_19.setToolTip(_translate("Settings", "The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre.")) + self.label_19.setText(_translate("Settings", "Gravity")) + self.label_22.setText(_translate("Settings", "Theme")) + self.comboBox_html_theme.setItemText(0, _translate("Settings", "white")) + self.comboBox_html_theme.setItemText(1, _translate("Settings", "dark")) self.label_5.setText(_translate("Settings", "Taxa Shape")) self.lineEdit_tf_link_net_func_focus_color.setText(_translate("Settings", "#B24745")) self.lineEdit_tf_link_net_taxa_focus_color.setText(_translate("Settings", "#6A6599")) @@ -335,30 +357,32 @@ def retranslateUi(self, Settings): self.comboBox_tf_link_net_taxa_sahpe.setItemText(6, _translate("Settings", "arrow")) self.label_13.setText(_translate("Settings", "Line Color")) self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1")) - self.label_11.setText(_translate("Settings", "Line Width")) - self.label_15.setToolTip(_translate("Settings", "The larger the value the greater the repulsion")) - self.label_15.setText(_translate("Settings", "Repulsion")) - self.label_12.setText(_translate("Settings", "Line opacity")) - self.label_14.setText(_translate("Settings", "Line Curve")) - self.label_18.setText(_translate("Settings", "Text Width")) - self.label_16.setText(_translate("Settings", "Font Weight")) - self.comboBox_tf_link_net_font_weight.setItemText(0, _translate("Settings", "bold")) - self.comboBox_tf_link_net_font_weight.setItemText(1, _translate("Settings", "normal")) - self.comboBox_tf_link_net_font_weight.setItemText(2, _translate("Settings", "bolder")) - self.comboBox_tf_link_net_font_weight.setItemText(3, _translate("Settings", "lighter")) - self.label_17.setText(_translate("Settings", "Label Postion")) - self.comboBox_tf_link_net_label_position.setItemText(0, _translate("Settings", "bottom")) - self.comboBox_tf_link_net_label_position.setItemText(1, _translate("Settings", "top")) - self.comboBox_tf_link_net_label_position.setItemText(2, _translate("Settings", "right")) - self.comboBox_tf_link_net_label_position.setItemText(3, _translate("Settings", "left")) - self.comboBox_tf_link_net_label_position.setItemText(4, _translate("Settings", "inside")) - self.comboBox_tf_link_net_label_position.setItemText(5, _translate("Settings", "insideLeft")) - self.comboBox_tf_link_net_label_position.setItemText(6, _translate("Settings", "insideRight")) - self.label_19.setToolTip(_translate("Settings", "The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre.")) - self.label_19.setText(_translate("Settings", "Gravity")) + self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network")) + self.label_2.setText(_translate("Settings", "Linkage Method")) + self.comboBox_heatmap_linkage_method.setCurrentText(_translate("Settings", "average")) + self.comboBox_heatmap_linkage_method.setItemText(0, _translate("Settings", "average")) + self.comboBox_heatmap_linkage_method.setItemText(1, _translate("Settings", "single")) + self.comboBox_heatmap_linkage_method.setItemText(2, _translate("Settings", "complete")) + self.comboBox_heatmap_linkage_method.setItemText(3, _translate("Settings", "centroid")) + self.comboBox_heatmap_linkage_method.setItemText(4, _translate("Settings", "median")) + self.comboBox_heatmap_linkage_method.setItemText(5, _translate("Settings", "weighted")) + self.comboBox_heatmap_linkage_method.setItemText(6, _translate("Settings", "ward")) + self.label_3.setText(_translate("Settings", "Linkage Metric")) + self.comboBox_heatmap_linkage_metric.setCurrentText(_translate("Settings", "euclidean")) + self.comboBox_heatmap_linkage_metric.setItemText(0, _translate("Settings", "euclidean")) + self.comboBox_heatmap_linkage_metric.setItemText(1, _translate("Settings", "chebyshev")) + self.comboBox_heatmap_linkage_metric.setItemText(2, _translate("Settings", "cityblock")) + self.comboBox_heatmap_linkage_metric.setItemText(3, _translate("Settings", "hamming")) + self.comboBox_heatmap_linkage_metric.setItemText(4, _translate("Settings", "matching")) + self.comboBox_heatmap_linkage_metric.setItemText(5, _translate("Settings", "minkowski")) + self.comboBox_heatmap_linkage_metric.setItemText(6, _translate("Settings", "rogerstanimoto")) + self.comboBox_heatmap_linkage_metric.setItemText(7, _translate("Settings", "russellrao")) + self.comboBox_heatmap_linkage_metric.setItemText(8, _translate("Settings", "sqeuclidean")) + self.label_20.setText(_translate("Settings", "Network Global")) self.label.setText(_translate("Settings", "HeatMap")) - self.label_21.setText(_translate("Settings", "HTML Global")) - self.label_22.setText(_translate("Settings", "Theme")) - self.comboBox_html_theme.setItemText(0, _translate("Settings", "white")) - self.comboBox_html_theme.setItemText(1, _translate("Settings", "dark")) - self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Settings", "Others")) + self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Settings", "Plotting")) + self.label_23.setText(_translate("Settings", "Protein Infer")) + self.comboBox_protein_infer_greedy_mode.setItemText(0, _translate("Settings", "fast")) + self.comboBox_protein_infer_greedy_mode.setItemText(1, _translate("Settings", "normal")) + self.label_24.setText(_translate("Settings", "Greedy Mode in Razor Method")) + self.toolBox.setItemText(self.toolBox.indexOf(self.page_3), _translate("Settings", "Others")) diff --git a/utils/TaxaFuncAnalyzer.py b/utils/TaxaFuncAnalyzer.py index 9e3b4d4..21b9b60 100644 --- a/utils/TaxaFuncAnalyzer.py +++ b/utils/TaxaFuncAnalyzer.py @@ -479,9 +479,11 @@ def set_multi_tables(self, level: str = 's', func_threshold:float = 1.00, 'outlier_handle_by_group': None, 'processing_order': None}, peptide_num_threshold: dict = {'taxa': 1, 'func': 1, 'taxa_func': 1}, - sum_protein:bool = False, sum_protein_params: dict = { 'method': 'razor', + sum_protein:bool = False, sum_protein_params: dict = {'method': 'razor', 'by_sample': False, - 'rank_method': 'unique_counts'} + 'rank_method': 'unique_counts', + 'greedy_method': 'heap', + } ): """ Example Usage: diff --git a/utils/metax_updater.py b/utils/metax_updater.py index 09d4ece..a454abe 100644 --- a/utils/metax_updater.py +++ b/utils/metax_updater.py @@ -204,23 +204,21 @@ def replace_metax_dir(self): def update_metax(self): - # ask if user want to update + # ask if user wants to update try: change_log_str = self.get_str() - except Exception as e: print(f"Read change log failed: {e}") change_log_str = "No change log." - + if self.current_api != self.remote_api: - QMessageBox.warning(self.MainWindow, "Update", f"MetaX new version is available with a new API.\n\nPlease download the new version manually.\n\n\ + self.display_message_in_text_browser("Update", f"MetaX new version is available with a new API.\n\nPlease download the new version manually.\n\n\ current version: {self.current_version}\nremote version: {self.remote_version}\n\nChange log:\n{change_log_str}") return - reply = QMessageBox.question(self.MainWindow, "Update", - f"MetaX new version is available. Do you want to update?\ - \ncurrent version: {self.current_version}\nremote version: {self.remote_version}\n\nChange log:\n{change_log_str}", - QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + reply = self.display_message_in_text_browser("Update", f"MetaX new version is available. Do you want to update?\ + \ncurrent version: {self.current_version}\nremote version: {self.remote_version}\n\nChange log:\n{change_log_str}", + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.metaXGUI.show_message("Updating MetaX...", "Updating...") # set update_required flag to True @@ -229,32 +227,69 @@ def update_metax(self): try: download_success = self.download_project_zip_and_unzip() - if download_success is False: + if not download_success: QMessageBox.warning(self.MainWindow, "Update", "Download failed. Please try again later or update manually.") return # replace the old MetaX folder with the new one replace_success = self.replace_metax_dir() - if replace_success is False: + if not replace_success: QMessageBox.warning(self.MainWindow, "Update", "An error occurred while replacing the MetaX directory. Please try again later or update manually.") return - + # check if the update is successful if self.check_update_status(): msg = f"MetaX has been updated to {self.remote_version}. Please restart MetaX." else: msg = f"Warning: MetaX update failed. Still in version {self.current_version}. Please try again later or update manually." - + QMessageBox.information(self.MainWindow, "Update", msg) # force close MetaX without triggering closeEvent QtWidgets.QApplication.quit() # close the QSplashScreen self.splash.finish(self.MainWindow) sys.exit() - - + except Exception as e: QMessageBox.warning(self.MainWindow, "Update", f'Update failed: {e}') + def display_message_in_text_browser(self, title, message, buttons=QMessageBox.NoButton, default_button=QMessageBox.NoButton): + dialog = QtWidgets.QDialog() + dialog.setWindowTitle(title) + layout = QtWidgets.QVBoxLayout(dialog) + + # set icon as parent's icon + dialog.setWindowIcon(self.MainWindow.windowIcon()) + + text_browser = QtWidgets.QTextBrowser() + text_browser.setText(message) + layout.addWidget(text_browser) + + # create button box + button_box = QtWidgets.QDialogButtonBox() + # create yes and no buttons + if buttons & QMessageBox.Yes: + yes_button = button_box.addButton(QtWidgets.QDialogButtonBox.Yes) + if default_button == QMessageBox.Yes: + yes_button.setDefault(True) + if buttons & QMessageBox.No: + no_button = button_box.addButton(QtWidgets.QDialogButtonBox.No) + if default_button == QMessageBox.No: + no_button.setDefault(True) + layout.addWidget(button_box) + + # connect signals + button_box.accepted.connect(dialog.accept) + button_box.rejected.connect(dialog.reject) + + dialog.setLayout(layout) + dialog.resize(500, 400) + + # show dialog and wait for user response + result = dialog.exec_() + if result == QtWidgets.QDialog.Accepted: + return QMessageBox.Yes + else: + return QMessageBox.No def check_update(self, show_message=False): print(f"Checking update from {self.branch} branch...") diff --git a/utils/version.py b/utils/version.py index 7663ef1..7510d50 100644 --- a/utils/version.py +++ b/utils/version.py @@ -1,2 +1,2 @@ -__version__ = '1.107.7' +__version__ = '1.107.8' API_version = '1' \ No newline at end of file From 68f22b50ae40126e6473717ca11de9a08b6f1815 Mon Sep 17 00:00:00 2001 From: Qing <44231502+byemaxx@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:13:20 -0400 Subject: [PATCH 4/4] fixed the msg bug after update --- utils/metax_updater.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/metax_updater.py b/utils/metax_updater.py index a454abe..acbac6e 100644 --- a/utils/metax_updater.py +++ b/utils/metax_updater.py @@ -200,6 +200,8 @@ def replace_metax_dir(self): shutil.move(os.path.join(root, file), os.path.join(metax_folder_path, file)) for dir in dirs: shutil.move(os.path.join(root, dir), os.path.join(metax_folder_path, dir)) + + return True