diff --git a/PKGBUILD b/PKGBUILD --- a/PKGBUILD +++ b/PKGBUILD @@ -1,35 +1,37 @@ pkgname=workrave-auto-hg pkgver=r1 pkgrel=1 pkgdesc="A tool to set workrave on schedule" arch=(any) depends=('python' 'python-pyqt5' 'python-dbus') makedepends=('mercurial') source=('workrave-auto-hg::hg+https://www.aboehler.at/hg/workrave-auto/') url="http://www.aboehler.at/" provides=('workrave-auto') conflicts=('workrave-auto') license=("GPL") sha256sums=(SKIP) pkgver() { cd "$pkgname" printf "r%s.%s" "$(hg identify -n)" "$(hg identify -i)" } build() { cd ${pkgname} msg "Nothing to be done" } package() { mkdir -p "${pkgdir}"/usr/bin - #mkdir -p "${pkgdir}"/usr/share/applications - #mkdir -p "${pkgdir}"/usr/share/icons + mkdir -p "${pkgdir}"/usr/share/applications + mkdir -p "${pkgdir}"/usr/share/icons + mkdir -p "${pkgdir}"/etc/xdg/autostart cd ${pkgname} install -m755 workrave-auto.py "${pkgdir}"/usr/bin/workrave-auto - #install -m644 workrave-auto.png "${pkgdir}"/usr/share/icons - #install -m644 workrave-auto.desktop "${pkgdir}"/usr/share/applications + install -m644 workrave-auto.png "${pkgdir}"/usr/share/icons + install -m644 workrave-auto.desktop "${pkgdir}"/usr/share/applications + install -m644 workrave-auto.desktop "${pkgdir}"/etc/xdg/autostart } # vim:set ts=2 sw=2 et: diff --git a/workrave-auto.desktop b/workrave-auto.desktop new file mode 100644 --- /dev/null +++ b/workrave-auto.desktop @@ -0,0 +1,20 @@ +[Desktop Entry] +Comment[en_US]= +Comment= +Exec=/usr/bin/workrave-auto +GenericName[en_US]=workrave-auto +GenericName=workrave-auto +Icon=workrave-auto.png +MimeType= +Name[en_US]=workrave-auto +Name=workrave-auto +NoDisplay=false +Path= +StartupNotify=true +Terminal=false +Type=Application +X-DBUS-ServiceName= +X-DBUS-StartupType= +X-KDE-SubstituteUID=false +X-KDE-Username= +Categories=Utility;Python; diff --git a/workrave-auto.png b/workrave-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..1ecd4ff4e781623f303c70de27608de647a0a237 GIT binary patch literal 11977 zc$^)Yb9Cfv8;7UZw%cy8#n#-~wr$(y*0#H~ZFB2RZQHhOyWhND&Pj4+@<%e6=e~c} zl{{hcvSRSCSg-&90A4~|SP``M`)`GT0&V+v4aPt_NJl{lWf;)n1!Eiv`uyEaT*DCn zfNS_~1v?l>(Fc8r=_I1=q-1OAM}iTMS^JPJR6NVaQCy?!ifL~V=w-*aMY(&-8S{UKNAv8wo z&~?jb8mC@xs1Bl(h1r+J$*rU@LaI;B7hHcJN#ehYH1Kuc%)773YI;lWEHOdYBh z96OfmdfTbEkU#-5MGO`R4|`S^mu}Gw$e!UyMj9ga>ZnUtsxpfT^K$uYRt#qlFrX9P zFX6u{&zAkRLmj_D#p!wBrV6u@M&QRF(DW)n!3UjPaoVDcEEu$(aG=xvr1BtKo(K^A zrem8c%%UgBkxO}`;EV)a81SU#a^I?L!c-bAFcc!JGA1GSq21l)S5A3&{ew`8;FmhZ zgemQ@JX1#A(dqW`ggXn2Im$Q#2!yHmccKS@u%bchaG7`X0#diV5IJA zDm7{AJ((r&ukz5(uP*@`l?oYMjf*+<2Y! z>UUGb7k2zSA`#AnB|58pOs|xutaWnf+nmeLy!a?xsO4I)WLLS#K!pi29uBA7T$5U@ z>4%K1SpE)Gp44D1LiG6vAOHK3D5!h2*h|Q-vG&?_&lM%vp_2~MJ1(|laicfpO02uY&|`_5P0m6 zf`Z=)3JN$p9*KKHF~1L#?s)R#XWgF98u0kgf=^fKOycYmE#l@|h!2lRi2NADgt}4! zTqSvd;XNPQzt3}c@nmFW^Bzu@s;6wbK1AzHC!9=HR#qah*`jlNUcdVh3WZ$1U3C>G zpWZ1gRqL7U^alUas1LcnFM;9$ay{h_9c>lJC#QdvBZ)tIn3!F~gllyzOK+!kXYdql zUkE}knNJ(5Mbm-)E>iQ&5_1O!dL|~L$xM#*HrJTxX?b1u15g>E>v_e^mB^jZ@cM;? zedqgp{#~inG&nz>i0|{QbyovDRvd_Vxaxpn|HX(0Z-W>0&j>w(|vw z5Y}fR0-cW0YLS}Nc2j#FXWo+K^YgQ{)pW7ZDW@aUQvf=w!z;O=8G7H?)X>H%COGi- zq)bj9=#=Z4jUwcfwk1e*D(b{6bdw75)ZlJR^iG|Ah_@*fOtUN8`S=tJalwsjwGF)$ zO9Wnj-?86XNgNL%esD~lkZfHa7u)Zpy5Ai#xZfHF-j7w~fY!uF2Oac<|tBw`Fx-PeLv;fkV zoDJUQ3?Zh63!e3?O!>0J{=tE`xU?d^R6&6b-9QT-8`$LNU{4Q=dCLIw$-~t~_}02A zownH~TfNTDS1V)`6pM{kmY$v-(WcAr{=P7kA3Fz+;bE|93)L6fyuU&H>pPU4Ffl$Pw)BD*!AP$_LU}td18ev2!;Xn{2lx zpKtc;ot&so(I_GB4kz}8@O=wSC(@ta-k6Msk#KNvM@C0?Zx6;GzkREOPb$DdVcU7mi5-&*@MeV(^w_q9oM`*0A=u3baxufl%`vD z_zgs$&$R-RKkMr1mRp^vl!~PdcKgH5SL*+sEmfmnV~+(wBR0F=k-j}%7|rC1Kte++ zRqOItF8dX?0X{hs%b9K$Wvkp~yI87~=78jR2bTKAAs?s^K$CKFD?g`YWH55r=2>WILYF_P+ zt5yJJd7(cl$i1GidQ0Bliwbb?v^Sysx3LIvj2OA2)= zXIhU=8|sS7V$cl^w{!UZP+98$^zit&@o+4mhcg$e?I3jZM@C~5Hui;7RzlyV(U3-i z&u=x`Ez_ZB+y>jNY(GE1`o>1Y8z)+{R+G)ir|rd|f;)s2Gd_gSEjl(1*dBOqdr|C` z0yvL6;F)69KmZqVRiA6(Ja0})MW?E*mQ(1Rs7A}rD z{Q(3o9uF*HNx(uaY;eFU(CV&wvjZk3a`$kfjhL9&?(^MU*SDh=_fL2*sBH~KVhUMW zQUap>c0~iVif~onHKTl7H_}wL$o6TNm%*4}InotN!_E*f2KS(6I74XnPgMk~bid_> zWZ_O=Gv@}&L1?nw7P$vPDKoK=i52AKnPq{$;Y_!n+>v(0I0JXBvL~cvH0VtjhbD8# zcq!6^{vz0aygGw2M159`QcYM)jLGda1%#xMWf{$JC z_QB56Asq1qd1zY9Y&-2IRMH@}+2Q#FLeGGJfa|9xGDbx`V?1|KjFLebN?W3M1tTFr z)5C?I$2t&>v(|5lKPJ2A-CQpEuHoM>TRq6KioIk%KIsfE@_nHy938|x-k156*IVs> zZ{GMyJaG~1{}7wXrdi-tbZF8D78KY6e-0V#etkH*xw~Uh_ws`~OGb9=@uRB{Z{9Cl zdl=Tr6$1z+=FsI7B~X>c7>aw|1F&OOp@j8+v_(TP9u5$CAP>Z#(~y!>8du#S?xC=| z?vVoe{vy&fDwYy$Z*O;VU9}4ph=xqgoJKl5%Hj=x*dd9Hut(~Z&ynHQuDK;l5?2dP z0Hc0xZkfRbeY}k5lvdwY719FzV?2<&Ijn9YaZ} zGU`8aahpL;Vu9ysb3@s{)oFr=L}&*h?YEg+@Q_g@zzcExW}0nwxxmWK&IS;ZlJ0JA z=gvj`cMU!t9$Byt3u<~Mpk#gq+6=66Xnu6>%n?RDPyNu@f+akjX@uul0~Q%%ak8s6 zkugzaq>9CW>Ww-B0cYPp5S+*J(fH%_VJZ`eoX_X&g6;j|!&x$4K+0#J#lGlyd0cO1 z#S?bF<(s&1*R37ukF~CZ$U;a^d(PML0&sD0jgOCu%gKRohQasKD~p#OrpKzfV-N(5 z!{Z+1a@STH;k>x3^;d56xf`2LIqV~!C}dI57pJGAbtYpNJRVMYs{B*1pAXf(wT?%! zi2Yxxj?8<=TSH`ocouECrJoJf0sI zOexx2;Mh_hR$FF!gAUJ5Fd8c>%WG>)7PG{enVIsGNrgp4dCHV!s+Cx}y1Jk~rQ1~b zsY37l>Vyj>oC#B!$UyQO&Ev7)tbx8|j^?LXa1Ln_qwQhe=YV`=$>N}^`>MMuARy5C z<$7XBPU=HMU_VB7QE_nEJKPhf-sleZ{rh*P%k{L&jkXl`swr46Z||GOM^a|y#QoAY z^Z8Rr2DX$^PCy1)OBZJ%urD-0jHBK#@6r|2rXRAQA-Rie5D|qipIaP{0LNQp1wHi= z)t!-*NoW9k-*@{wQ#D@^?k8=VAZ?17QYwnxc0s2gvL*lp+`=1raG~c8PWlV4;@v#& z*6eNByPo7oqxG6oSJcXNrA}Kir;Sa6g&}!EEwIjTJVKLaa3q0Dqux|TqxfxnXXJ(q z6;|2wFp04^u>nkeJWnWACj*i7bIa-BkB@y#|nKS__ zYqIy-<4qqTUu=AQZ@EfEv%>-GUI@I10^p9-^cYFok{z(BLr|SCsE3P*xwees-G7dz)W&5hl+ zr;~{ynJ(mZ7nV)n50y;G-~bMkt>Ut#Pm?_mwG(tFQii%7nDG+K{E@Bs7AgW=FW zi`>DYMEgL+cyn{JlcLuUy_j^!6blzT|4a!vTYp?0658b*m=JLUPH(sk$XZGZN!X|9#&<2kor zpjJO$Q*ShZUXQsb^>)xXTTN%6~N?S-0~J3$&iLt8|Vxs&FZb1gGkj z21nzqw{!V&smbNLn> zlrl=~M?h0pQ6VL=pAVqqy!q?2P4`!9*Z6_18_IM8U=tnf#c0Z`B-W|AI{@A6{KYE> z1Jc0VM1Pb1ZhwgH2nfZpS)8qPQe9Q$4LbaO1>j*qVVV*W%ER^m-E)N()Jyqgut4kOgE> z{gvctt#GtoV>rfYIbV7+uKt>gj>Tfx@B8&OpYb+68Z3Xav~CGYOrn^>v$I&Si_mHF zgx5<%^t_)S=cG5b^}RqWW|3kB*=CEF_gqv2(W2Nd{-+<%lO{+|!cY?SM&bagrCpww zPxfF9cvh-Zf&hww>IQo%6w0LVp1_>eD86Qg&iud3RQ9>zMPU?;z=j`V!0T%w%M|=y zRa^XCii|nFpDmL;w^yO0GTG%Ob#s)Pp6X!6Xc|=brw`MI?&}R-{O(gjUF}kcn2c{jg$9Rmy^u!9pPU2C?Fl5-+h!(^Q_YJc%hE{*uiqTueKSmELGES3Lfagn7^o@qRl-cYiV` zW=2~gUv|uEWl$7cgdWO88I0;$NS>iQ1vKHx)~gNft}aILVF^9lp8fBW`Ql#5Kp z^ypH`Y2$#>`hNpS(~Vu=XtLI22@j8i)X>6Wq(2O0jCsgGfCHr)6vX$2FdhvT;EKq; zZ%~J$q8Vj^%lGFP;I-{lu5TBKBmI>C0ZOR)~m* zif6(UBoDjQ zVLGwxMJS02#LD)?d&Ed7GC#i<`LiB+-kNRhAhVNLi%8$$0bd`19#tM$KTe}g?~B%y z-biVu9`-|XrNzfA>G3Z+P3(az;3rv3o$F}EkCV%xnx>)Mlgp5dg5Q+Cvjxsvu67E7 zx9UyCeig(uk9|`r>1}s^HQ8vxCiBbY+0pkWbcu+`#~3J6ilX(2u1ArujV3Bk5;F$t z44_D%LwW2`hT5lMn-DkajSL(Aw1Adah&n@o+$BB+7aleWo~m;SoHw z_ph`bAyY*LzrLm1Ox`L%yuXBxj+R4TpZS@Y*&r;BR2cLfg&+rg0d1S4D7-%qT4H1Y zt~62d)WdCCYYAnm)%m9EEG(vkYz+}wU#*(3tK$pPCeHPSr}7Keb-6xRNJz|XYi;_8 zOsnz-R0j-U9-AL>#tJOOO}mHxFaNijPNaaLN~_d^u-7s$#trS62Ahe!Bj|$1X~bV> z*yv(z#;7wfc=kJoGOB-8M7KJ<|9eu?z~xs5le}mJ1g2M}F^f^Q=Qmx#-3pa5)(Zgc=8SaC1QbUg@T5< zdDs2BmXF0Z`au@}1u)X!Q`~T&LdZwAe*yT;9DvP=fg0S>VoyyO?yIGKY)l5!);UfY zGT{YDS8T-+yU!>15S{M+?E>8UqoOPK$16kb5r6CD%#`>1`7+`7oZ?97<51g<)@BaQ-OF=f8gz8a zR`$EM9Oz|_V|qeD!c}UW$w@RRfZQvUEO|>j4xxy_9BE*FWlOu9R8Bxau!ffA?oun7 zPbCn?XS{%2_0Ps=IL(?m$1%xm#F#WAlR2J~A4*5gQj&%I>17coIKA(9oD+6@EUHH{ zD;(!=>I%MovToh7xBHBsUEbm_nM}99k4DZAQm|7zPjPe$7AtpWMSswn^}9*u znL=w33-;DWP>c^>ukDreky8xVyF1$tkv&nToOj+BKDU^^TN7eN{i%JgN6w*Mr$#AQ z2<^-D&=L|7?Kgwi;zpL+b>s%4g7P8_S*+GrG+K@QV`C8)Yj7{gk*V<}5H`9GZf|-; zQo}xEl(14_W`>qaEN+JmY(L`K&JM_*S+1CPz)RCmS`dR~g4~`?*%*;1wXGKXU8YFZbDu)M~xpTn@)E z4GqJ~Y{_lGLnG!!ejuZufK@KtPUi_}G?>TT_(Tq4IQWU+%P^fvX`tejlsHn{|3Y}c zzUdCdOw$nMY+T)6W}vJu>~qgK`PnVa#|ISt{PQQ4nF`w{vm`5cqM|LU-s zsHJr$+4A6A(zug}wqC@MQ433SkQMOIXK!sQ&)+Hx35+zeo=x{7Tuq>* zNW?Fi(9lqNR%dTiegIZf2nf7SW?N9(Dn$!AMErE`89}mCD3O3cuMhBix?F&>C}7P$ zqeln7N-vTks3oI^0g47%)w32ZW6AlIjZT8^kG`)6!;_#%)&_tZ4hFN>^z7r3xjn_y z|G|9&O9!9L<9uNyJKyw%xXN<^rKxna4%4a_E*l%`AZJF%HaFKFiNkRWxe`rdDDuU;@Rc9F}GNDo_V6aH=lXts>c;hCb$~l6B!Q zCcEqZ(9&Qgb?4-!vMu`4l+jSEx}muR0y&*iKX*_A9Z)DZYF+8tTrOs*^qOpNDS`{D z=VT|@!L&y&j02Dq{hS!&UyTTpk#97ZhJ6!h|X#?#{+QKRD~?jQz#4wCuEUT>5H2FDyh;w zJWi_*%Hj@Y;L|W8=n@k&G$e979H)4O3lWU*Yft5NXMDQcFdB@+WM|*^nC5E{+3)Hd z7_75iW8L&Pqp@aDVuGUkz+FqvZgm~^%9M6VXJw0*ae|b_wWfz(D>PobS{ZxgS+!~{ z=-{F8#+q&DWP9M|Ef;Rg>~BBz~oMBG)4^FF_Br=38Gq-?pe`5$YAeMizbd;3foDYD~r_vkD> zx#0yXV^a&)V61O$L)>4FPTXgUWF({kTD~@9hXKiH?}r(-=F2sNzr~`Nc^Ql#uaAG; zi1toUYd6O(RH)s7$a}8dOfH?(I=mzhqTCDe`l7)?+NU=KM&L~%hg;`;osr2(Yy6+1 z2sQ95&eTK*=#}>%3je`|>GKiV;guY;#wO9~``3?Z$+`zv$b>`YoD?*xO~TvL46*|D^ChAe;? z=N_5uYq|I4TfMwlA(Q1iIT6T0pZ8+mt>Iwx_Yp9DP6V(H-pm$BX|y^~0N%g?gQ{C1 zYz-|${z@=iU$Qj}cTH`CMBR+QH{3u*2}SQQ-xvilGp12U5N~t|IrQTy;t%FeiVG!V z$#`pdp1NGcB;6@d6f>OsQznmM(nJ*mP_??*;^Sg?z>+6%R8ew8_)OZ||Cx`Ek92CVe^&6mhuq9VKN=p2eFFR z*T)mH?dH411~>-Cowj{_U=yNF@zlWRK-PX}n{Wuuo@{pxcdI=nzzAJb4Fv@Y(LIUu zvmjzT{1cMU=i>)VH_bb#%}$=M`>7U0y2je>{@RZcBJF`7(ZHz{{XmDYe)he283Qf= z0dbC`z|Nzz`?k*;sZ2o3^z|S5P4ubbf9eqdt`nTvl=}zq7gpP-c{$%UNH?dmdO)Tc znEXDtNl_pk-7F<9j|2*kLb)8?NDKyWK0ZEeI3#E;2p8bl%vT#Qb!GYV)$!g!^6n>! zxHpE7_Av`;S5-j$tJ;96jMkh@zAj@Gk$s~TzODwnlw1vIVd2wY(!Xq77(^u)O-Q_M zT0Jkq?}~Xd|L96(@*r#4Bc3{E#B47GLcuWwBj$%1IBFuI+kA)aRtEyg(O``t($Gg1 z;QKIEh%s!9_H-lAlWlkQcdJ`!-&_k5&o|mqI$B)ZlarIzlS2ICPB~k4YxIFOH+Oxr z3K`fl_meab;59PjEe6L%TAq_mXHppuuWV^9pd#81R)cx6(9Hjou4BZ!Psp+>-Ep^4N;{N8}VJ+HbA>o#TP3vD#nDyo}rOqu6q> zfgE`Z=`HRJ9h@HxnVdVMzkYj|Ki&R2+y`A)T#RhgFIcj6_o>eLD~q0HrSrL89hEgE z$I`AywR|Bm`@`Q~u!J5aD&*%^mcQ8Y!+Q+zwT6LpL?Tz!`L?r_^mgSH@ogWOiR@2T zw=+IlY#>Cepk0Xp3a!(rfO-Amy2Z5m70KGwhU8gb+ruhq2LuMP-^OntYfYbKW~*SS zdeuVV4YFNjg4&d+8+Ow8=jn>rn35V#?RN64!9&Be#83%U(v@NoIPITT=YyMSLyT@k zojoB+{W`pOY>{8yEkSQ0#PM~W_kMADG6gdfLSd9O%KUkUJ_E?yS`-te#idLkMAzl6 zFVt6F%m@{Q!X2Czi;XyVx0GOX?b3pTdhxY<`U;TO!{Qz)L&I&NmRNs*S~6a`Zr^gW z*Es4mu7o-(n@Tyj1pHrNPR-_F|or0fCUD4&dvTsu_Ys3mSWFK{{mlx+98} z9R0$x*Z=a`yPDUYA!0big9S@;JOgCgeAy%cI!(5P4?Q~nNdSdtG-8zCNVr_rLB~iz zsqbgvCR5(b^?FExrcY^;T<_0;ml$pmHU8xLL`2iUSpAKBAJ5_Ahh z8}eq!=kl?@ME_ac2^A>M5I@5_#@x4Dq&HRV;JBysV@;+Sn@n%7Q|Nx$u=22eNk^rM zbNAZV)h!Veya1Rb(Mmkv2Z^Z?Jk%zWxa}Qj9h4~r-(SCZ=yuIZ@Yhnig!3drS~V=` zZ}?}Mn)J(dgQ&<5|*bEHOQ1;ry({yn-WaUT>=;Q&PxCMbc@* zi*v@dYg2HPeZm+v(rObcJ?Y9%at(VQlN1D)JmNBsjP7T}3}RFBqD};5opQv2@qHnt zw7tSsuCmcGIqvf#z~lBLv1W-o*a?@Z?s@TLwyM!h+ibCR1dlQ|>CY^UUF+R{d!Wsk zOQK@tP$wb5;3>KLy*3|-fA~2(A69Lcr0f*?51VGS&Svzef1c9EGURQO_s{*O@Kc*+ zxT+!r8tg1h<(*oJ;Ps&c-<2VT@Noe#r2cg=i>4J6^Nqo< zg5w{qdx)7=uNR8?eT95fRL10oKmJ#+SE~n_QA}wh}Pg zil5+X&wrhk+mdp@g5Pdig8woa405$rFQtn^J2^C$t2BTf5igTK07QN++>x;b395%aquv)pSy#4ay-r5L&9=~`${ zJxrko=$XQ0uGl|U`pTQ~IL6BL#OqC%J81Yza`*v43-JTPX@chARoe3CBz{2#-B*Lz z11-%cCnqw=E#FvgVoK3G7Wf07x(itEepUYNnF($xk;`?To)a%X=X$>RSQ-oGK|c#( zZ?)BsYV%%;(mlg#UD%ggqlH9xMSp7dj@Y=;gCQ(FrSAHAVWCvwUO~o}MqUJ~PHxyp zv+;g$U&YC)DV^}w0Z~`yLD;{my$;L6^aTaeC%j@W z($;E%pro2EbPC^qT+9}s#IZS&glj9itNw-lFe>MIB?lZDQI3fs8mRIpYtr)rc^7wM zBA8K5_XeaB~Bb=jJU^;(>-0-qBl6M}4LJ1+dhV zC{##D^3_KzQMPnG3)l53g#-tv?;PPODB+$!j|*b69^+&Dg{&1fh?}=4%bCg=#i*H` zI%E(NYLo0S%)PCb%zS_mz&WkpujlBX(%AosL5yqmY z8sV)_F`?sCW^m=DJZ{ZUq&$**e%tq_!E~Vr+PyHzI!&T56VkYr)}Gqr##$7DdBGI# z>vs2*%jEz4S)X6AlvkcT%p?GiyEg5fpcN$KJ-nh4;SQD`FaL4b+3Z#fv;AH4+opN3 zFoQ-fRbscp3uZ8+y*v?m<@vdeeHu_Q%q$*|y&dJJ#i9cH5+2m#H+vvb#u#cI_O4N; z|56vdd5|7kzY#Ggrxx&YwBe4hh5DF7_}*u=ZoO#0tuFpR$EzNORs)YNB!q7u}61S}0H zm`vhXupo$4eiF)tP1#&D@iKqrSbOlFB?5!6dCf7RAyS0B3glIl=ju(*cxApn-|-2* Wv459dL&*hz76}nq;VMD>fd2s}4UEMA diff --git a/workrave-auto.py b/workrave-auto.py --- a/workrave-auto.py +++ b/workrave-auto.py @@ -1,256 +1,337 @@ #!/usr/bin/env python from PyQt5.QtCore import QTimer, pyqtSignal, QObject, pyqtSlot, QCoreApplication, QMetaObject, Qt +from PyQt5 import QtGui, QtWidgets from dbus.mainloop.pyqt5 import DBusQtMainLoop import dbus import os import configparser import datetime import time import sys import signal def wk2int(day): if day == 'mon': return 0 elif day == 'tue': return 1 elif day == 'wed': return 2 elif day == 'thu': return 3 elif day == 'fri': return 4 elif day == 'sat': return 5 elif day == 'sun': return 6 else: return -1 def time2diff(now, time): datetime.datetime.now() to = time.split(':') hr = int(to[0]) mi = 0 sec = 0 if len(to) == 2: mi = int(to[1]) if len(to) == 3: sec = int(to[2]) return datetime.datetime.combine(datetime.date.today(), datetime.time(hr, mi, sec)) - now class autoEntry: def __init__(self, options): self.options = options self.next_action = options['default'] self.next_run = 0 self.in_interval = False self.update() def print_options(self): print(self.options) def update(self): self.in_interval = False now = datetime.datetime.now() days = 0 starttime = time2diff(now, '00:00') endtime = time2diff(now, '23:59') if 'weekday' in self.options: curr_day = now.weekday() wanted_day = wk2int(self.options['weekday']) if wanted_day == -1: print('Error parsing weekday') return -1 diff_day = wanted_day - curr_day if diff_day < 0: diff_day += 7 days = diff_day if 'starttime' in self.options: starttime = time2diff(now, self.options['starttime']) if 'endtime' in self.options: endtime = time2diff(now, self.options['endtime']) if starttime.total_seconds() < 0 and endtime.total_seconds() < 0: # After interval, if it's today, we need to add a week if days == 0: starttime = starttime + datetime.timedelta(days=7) endtime = endtime + datetime.timedelta(days=7) self.next_run = days * 24 * 60 * 60 + starttime.total_seconds() self.next_action = self.options['mode'] elif starttime.total_seconds() < 0 and endtime.total_seconds() >= 0: # Inside of interval if days == 0: self.next_action = self.options['default'] self.next_run = endtime.total_seconds() self.in_interval = True else: self.next_run = days * 24 * 60 * 60 + starttime.total_seconds() self.next_action = self.options['mode'] elif starttime.total_seconds() >= 0 and endtime.total_seconds() >= 0: # Before interval self.next_action = self.options['mode'] self.next_run = days * 24 * 60 * 60 + starttime.total_seconds() else: print('Invalid settings detected!') def get_next_action(self): self.update() return self.next_action def get_interval_action(self): return self.options['mode'] def get_next_run(self): self.update() return int(self.next_run) def is_in_interval(self): self.update() return self.in_interval def load_config(cfgFile): config = configparser.ConfigParser() config.read(cfgFile) return config def save_config(config, cfgFile): with open(cfgFile, 'w') as configFile: config.write(configFile) def recreate_config(cfgFile): config = configparser.ConfigParser() config['general'] = { 'mode' : 'normal' } config['entry1'] = { 'name' : 'workrave', 'mode' : 'suspended', 'weekday' : 'wed', 'starttime' : '07:00', 'endtime' : '15:10' } config['entry2'] = { 'name' : 'workrave', 'mode' : 'suspended', 'weekday' : 'thu', 'starttime' : '07:00', 'endtime' : '18:00' } save_config(config, cfgFile) class workraveManager(QObject): - def __init__(self, cfg): + def __init__(self, cfg, parent=None): QObject.__init__(self) self.cfg = cfg + self.parent = parent entries = self.parse_config(cfg) self.entryobjects = [] self.default_mode = cfg['general']['mode'] self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.run_action) + self.inhibited = False for entry in entries: entryCfg = entries[entry] entryCfg['default'] = self.default_mode self.entryobjects.append(autoEntry(entryCfg)) @pyqtSlot() def run_action(self): print('Timer fired, about to run action: ' + self.action) self.set_workrave_mode(self.action) # This prevents the timer from firing several times time.sleep(1000) self.setup_timer() def suspend_handler(self, suspended): if suspended: print('Going to sleep...') self.timer.stop() else: + if self.inhibited: + return print('Resuming, doing the recalculation math!') self.startup_check() self.setup_timer() def init_dbus(self): dbus_loop = DBusQtMainLoop(set_as_default=True) self.system_bus = dbus.SystemBus(mainloop = dbus_loop) manager_interface = 'org.freedesktop.login1.Manager' self.system_bus.add_signal_receiver(self.suspend_handler, 'PrepareForSleep', manager_interface) def parse_config(self, cfg): ret = {} for sec in cfg.sections(): if sec != 'general': ret[sec] = {} for key in cfg[sec]: ret[sec][key] = cfg[sec][key] return ret def startup_check(self): + if self.inhibited: + return + print('Startup check...') in_interval = False for entry in self.entryobjects: if entry.is_in_interval(): action = entry.get_interval_action() print('Interval currently running, setting mode: ' + action) self.set_workrave_mode(action) in_interval = True if not in_interval: print('Not in interval, setting default workrave mode: ' + self.default_mode) self.set_workrave_mode(self.default_mode) def set_workrave_mode(self, action): wr = dbus.SessionBus().get_object('org.workrave.Workrave', '/org/workrave/Workrave/Core') iface = dbus.Interface(wr, 'org.workrave.CoreInterface') mode = iface.GetOperationMode() if mode == action: print('Mode already set, no action') else: iface.SetOperationMode(action) + + def get_timer_time(self): + if self.timer.isActive(): + return self.timer.remainingTime() + else: + return 0 @pyqtSlot() def startup(self): self.init_dbus() self.startup_check() self.setup_timer() + + def is_inhibited(self): + return self.inhibited + + def inhibit(self, action): + if action: + self.timer.stop() + self.inhibited = True + else: + self.startup_check() + self.setup_timer() + self.inhibited = False def setup_timer(self): self.timer.stop() next_action_list = [] next_run_list = [] for entry in self.entryobjects: next_action_list.append(entry.get_next_action()) next_run_list.append(entry.get_next_run()) sleep_time = min(next_run_list) self.action = next_action_list[next_run_list.index(sleep_time)] print('Setting up timer for ' + str(sleep_time) + ' seconds and then running action: ' + self.action) self.timer.start(sleep_time * 1000) + + def quit(self): + self.timer.stop() + QtWidgets.qApp.quit() + +class SystemTrayIcon(QtWidgets.QSystemTrayIcon): + + def __init__(self, icon, manager, parent=None): + self.parent = parent + QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) + menu = QtWidgets.QMenu(parent) + self.remaining = menu.addAction('Remaining: ') + self.presentationAction = menu.addAction("Inhibit") + self.presentationAction.triggered.connect(self.inhibit) + self.exitAction = menu.addAction("Exit") + self.exitAction.triggered.connect(manager.quit) + self.setContextMenu(menu) + self.manager = manager + self.activated.connect(self.tray_activated) + + @pyqtSlot() + def tray_activated(self): + rem = self.manager.get_timer_time() / 1000 + sec = datetime.timedelta(seconds=int(rem)) + d = datetime.datetime(1, 1, 1) + sec + if rem == 0: + self.remaining.setText("No action planned.") + else: + if d.day-1 == 0: + fmt = "%d:%d:%d" % (d.hour, d.minute, d.second) + else: + fmt = "%d days %d:%d:%d" % (d.day-1, d.hour, d.minute, d.second) + self.remaining.setText("Next Action: " + fmt) + + @pyqtSlot() + def inhibit(self): + if self.manager.is_inhibited(): + self.manager.inhibit(False) + self.presentationAction.setText("Inhibit") + else: + self.manager.inhibit(True) + self.presentationAction.setText("Resume") def main(): signal.signal(signal.SIGINT, signal.SIG_DFL) + app = QtWidgets.QApplication(sys.argv) + app.setQuitOnLastWindowClosed(False); + cfgPath = os.path.expanduser("~") + "/.workrave-auto/" if not os.path.isdir(cfgPath): os.makedirs(cfgPath) cfgFile = cfgPath + "config.ini" if not os.path.exists(cfgFile): recreate_config(cfgFile) cfg = load_config(cfgFile) - m = workraveManager(cfg) + w = QtWidgets.QWidget() + m = workraveManager(cfg, w) QMetaObject.invokeMethod(m, "startup", Qt.QueuedConnection) - app = QCoreApplication(sys.argv) + + # Wait for up to 30 seconds for the systemTray to become available (required for, e.g., Xfce) + count = 0 + while not QtWidgets.QSystemTrayIcon.isSystemTrayAvailable() and count < 30: + time.sleep(1) + count += 1 + + trayIcon = SystemTrayIcon(QtGui.QIcon("/usr/share/icons/workrave-auto.png"), m, w) + trayIcon.show() + sys.exit(app.exec_()) if __name__ == '__main__': main()