From a265b71d36e462b85910ccecf2f1d9faab6f61eb Mon Sep 17 00:00:00 2001 From: glubsy Date: Wed, 28 Oct 2020 01:45:03 +0100 Subject: [PATCH 01/18] Improve comment reflecting modification of function --- qtlib/preferences.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qtlib/preferences.py b/qtlib/preferences.py index 7e3ba864..3f33705a 100644 --- a/qtlib/preferences.py +++ b/qtlib/preferences.py @@ -121,8 +121,10 @@ class Preferences(QObject): self._settings.setValue(name, normalize_for_serialization(value)) def saveGeometry(self, name, widget): - # We save geometry under a 5-sized int array: first item is a flag for whether the widget - # is maximized and the other 4 are (x, y, w, h). + # We save geometry under a 7-sized int array: first item is a flag + # for whether the widget is maximized, second item is a flag for whether + # the widget is docked, third item is a Qt::DockWidgetArea enum value, + # and the other 4 are (x, y, w, h). m = 1 if widget.isMaximized() else 0 d = 1 if isinstance(widget, QDockWidget) and not widget.isFloating() else 0 area = widget.parent.dockWidgetArea(widget) if d else 0 From 92feba5f0821ca330ca85614b9c12a505ac728f3 Mon Sep 17 00:00:00 2001 From: glubsy Date: Wed, 28 Oct 2020 01:48:39 +0100 Subject: [PATCH 02/18] Remove obsolete UI setup code --- qt/me/details_dialog.py | 6 ------ qt/se/details_dialog.py | 6 ------ 2 files changed, 12 deletions(-) diff --git a/qt/me/details_dialog.py b/qt/me/details_dialog.py index 61c452e7..ff36c37b 100644 --- a/qt/me/details_dialog.py +++ b/qt/me/details_dialog.py @@ -19,14 +19,8 @@ class DetailsDialog(DetailsDialogBase): self.setWindowTitle(tr("Details")) self.resize(502, 295) self.setMinimumSize(QSize(250, 250)) - # self.verticalLayout = QVBoxLayout(self) - # self.verticalLayout.setSpacing(0) - # self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.tableView = DetailsTable(self) self.tableView.setAlternatingRowColors(True) self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableView.setShowGrid(False) - # self.verticalLayout.addWidget(self.tableView) - # self.centralWidget = QWidget() - # self.centralWidget.setLayout(self.verticalLayout) self.setWidget(self.tableView) diff --git a/qt/se/details_dialog.py b/qt/se/details_dialog.py index 8b910bc3..c30c4b90 100644 --- a/qt/se/details_dialog.py +++ b/qt/se/details_dialog.py @@ -19,14 +19,8 @@ class DetailsDialog(DetailsDialogBase): self.setWindowTitle(tr("Details")) self.resize(502, 186) self.setMinimumSize(QSize(200, 0)) - # self.verticalLayout = QVBoxLayout() - # self.verticalLayout.setSpacing(0) - # self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.tableView = DetailsTable(self) self.tableView.setAlternatingRowColors(True) self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableView.setShowGrid(False) - # self.verticalLayout.addWidget(self.tableView) - # self.centralWidget = QWidget() - # self.centralWidget.setLayout(self.verticalLayout) self.setWidget(self.tableView) From 59ce7403697b24415bc2952514de650dddcf731f Mon Sep 17 00:00:00 2001 From: glubsy Date: Wed, 28 Oct 2020 01:50:49 +0100 Subject: [PATCH 03/18] Remove print debug statements --- qt/pe/details_dialog.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qt/pe/details_dialog.py b/qt/pe/details_dialog.py index c83ee754..ee476528 100644 --- a/qt/pe/details_dialog.py +++ b/qt/pe/details_dialog.py @@ -129,11 +129,7 @@ class DetailsDialog(DetailsDialogBase): # This works when expanding but it's ugly: if self.selectedImageViewer.size().width() > self.referenceImageViewer.size().width(): - # print(f"""Before selected size: {self.selectedImageViewer.size()}\n""", - # f"""Before reference size: {self.referenceImageViewer.size()}""") self.selectedImageViewer.resize(self.referenceImageViewer.size()) - # print(f"""After selected size: {self.selectedImageViewer.size()}\n""", - # f"""After reference size: {self.referenceImageViewer.size()}""") # model --> view def refresh(self): From cf5ba038d77ed3df4633fe5af027bfacd33f27df Mon Sep 17 00:00:00 2001 From: glubsy Date: Wed, 28 Oct 2020 02:18:41 +0100 Subject: [PATCH 04/18] Remove icon credits from about box * Moved credits to CREDITS file * Updated exchange icon with higher hue contrast for better visibility on dark backgrounds --- CREDITS | 2 ++ images/exchange_purple_upscaled.png | Bin 9042 -> 17045 bytes images/exchange_purple_waifu_s4_tta8.xcf | Bin 18409 -> 17151 bytes qtlib/about_box.py | 15 --------------- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/CREDITS b/CREDITS index 731c46e4..8e333f0e 100644 --- a/CREDITS +++ b/CREDITS @@ -1,6 +1,8 @@ To know who contributed to dupeGuru, you can look at the commit log, but not all contributions result in a commit. This file lists contributors who don't necessarily appear in the commit log. +* Jason Cho, Exchange icon +* schollidesign (https://findicons.com/pack/1035/human_o2), Zoom-in, Zoom-out, Zoom-best-fit, Zoom-original icons * Jérôme Cantin, Main icon * Gregor Tätzner, German localization * Frank Weber, German localization diff --git a/images/exchange_purple_upscaled.png b/images/exchange_purple_upscaled.png index 1f31231c8823be822a6ea331e97ac8aecb52f2ef..f351af13a1245c2f3944180e5a32d55ba76e57e1 100644 GIT binary patch literal 17045 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 z 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 diff --git a/images/exchange_purple_waifu_s4_tta8.xcf b/images/exchange_purple_waifu_s4_tta8.xcf index f3c922cba7889c45b6b19d6c00727b2abffc14a7..89e3ecdff41d661b0bfbc164c41f6328b6de124d 100644 GIT binary patch delta 9258 zcmb7~TW?!ecE=C*IlSmXNfag7v~#GtD9abwy3nyKh0SFw^SMA2Iw~T8?WJsDY!s_F8MNec|DM z{kgXP{ckeq!_43QN#@?yfBJ8kzkb@toXZ3^w>ED)`F&>o$zPe`uv2;RkEWjakEj1) zE@k3z@9f{N5#6oOfi_+_aqY@KyY}WI*Lt4s^*;LHe)pzD z_~g5;6YhO4g7HrMUwmuh&Q+TYeS7lJC-;8w`QuHS3ynSg;k)m@ef(wTn$3jCV$F!^ zez$9Fr+d|!@Oiym3&X0lrki$*O}kf6o*ddH$~r@iH`=zn7>2v3(>e~!H_Yi+|1jgiKqjR+qLWJGGf!a zDm9VX*PVS&O}BNX+b^r)*XW$;n(bBDP(i(0S8Ncb)x3te!Xc^Dylnljx4~BPF6IXP z)B2riSk0-Y^!xQYOD5EQ^OE#;@2vP?7CReVk5zMb-5+?YYya>vr^Rf#w>75j@3imu zp{ymXOTV|)G4NNv#%l1UI9DUjdG+fLg zmKdJY_48N|tqD)pws)KD(CK}f-ZtTwO{>|kzk1ldFjOb4stL!@=fdN~t2efHn(Z0c zR=w@>ajclIWJki@%679orMqp3`idWx?P%Dm;XQ8sI7|=u=$B!cC3b&Juaigx7FR4|6vC*}zgiv*>wadTZd;%rRzx z_wSmEhlzOF*=Xl&y6rk?vtawz!hxYhXU%oey9M4aanetT&fBh&)(dVmmu%KcGFjv8 z7~ebLOO3eeMA7b=tk;W8V7$vnzB%;*e2mG^&(ZRoHNNq#YNwy^GRE*1V?o612?IUx z^}UoQV^YQ>JTIvN?;0ivl^AbpAa0ugRiOPKMk$6sTjS*ze*<~g=2?#PrM*r&!Za_p zd+2Ttp4Z7Te}U%;Tg+>l)Aq8-Ya4H{og9V@GotM!Gs-yy-a4)5rImqeW{f~)(E8pg z$CBbF!B+^L;SqV6meWyrN#z`J-V2d^>|@MxLFK?3HxscpLlB>*WQyLD%2}^$CNKt^ zN`P~LL0LVaD(&b%USct$*s}!P)K*SA&_%!6cltBC^kq}mOf1J^bf9b9aSe~{8nyz? zm&_mhOvd}{+wQ{$!0@(BGsb&<@9XZv&RO@DzvVuBViZ)}8D@QgOGX|_TX$@RiFb1qQ=>hTqtu-CKqb8|D#E+(p0qzax0|JR0XT-#} zTc!l>2?EAr0^^+=%{aLM#y1dUz!7pZ3(DgH%I~YxK#IV72?*X}0?3&Pyq6(+R=!TJ z>f$p$0H1F`7LXNp0N%czUkNY#*NW_ z1IRbWwf!kMWI$gQ2gaNJ+WxGMi_G5;#qrv~1$>xM1FzAX5P2=y_l+zmt%3Wdw{{d2 z4T@ftS;2jCS~)nfx5P&xf%^c8sU2Axp4A83XVSj8VxLfZy~c zR~r$)FNevP=%Q}`e$y-C4e;}Z@DA+9z5)16ItuVh0CbDS0Q?5vcZy!bVFLVUbPj<3 zwlSObloPE)Ab-T?0rC?O4Br#ez%PjkV$mgF+B$PfqP(;G8FOOdO?7?|$iM75X#=pY z8h;RgPT6(Reao*RWig=R2k6U-PH!rVMAs&mH+lTEr3_B*Aw#f&J#jv+{UARCJw;_*H zHfb%F!hWsUE-T5KA_@E92rR0<(QKDk?0Se7@DUc-B0DO{iH4K54+)$S`m@n&P3lh}TAYOh(46RL{lvp(Fk@A1G5-g)60X zr^~`I?zTsDnX}6BhWbp{TZJm+hYN*ltm7zwvh4U|VP*Z=9p0V(b=ii{!6sG~o#ow& zPnNfuZOehosHyHu1x)?rt(vSBM!AcYEfsABhL^(WRJ-GRr))v{%sQg8@)^Psb@4E4 znRbSCxXtjr5SAt+N_bNmbKYnCXluZab{V!2LfwST%8-S#PI~C;2r;DBhO*FmUbU=^ z{VDSIDkP*}%HASv)?6UB3p<)1jgNa5kyCt%@PyR#BD-z78 zG0VilK*SmN|VW%96EfjR-6i{kg2EkQI&zTd5r!0jE);k~6@mWE|j>Zq@*&jkQ{> zaRi()suCE`G^%9{XnNGpE{^AX1DZx_uWiU~VyqbhoJMofqz%b)%gq4S@QXKb@db_^3K@Xss0a)9bEaQzM0^_|OMvH= zZ$MPjuQVbMRpvq&L#m24F}@~r0E(4>sv0IR{d0{Yu#@rpg|~sKvVTxjGA=r@BB<(g zk`+N!SpuLcIzUy2_W0|As?ZNmRd^hz8bNqwh24-J0nrs*4wM-nRKGqeVS&sb8l1pT zRVC5H(95Y<^?`4IRbJsr1T1ncB|b+&`YxK&0jokU17RX*&FNxrHQbNDRn`cuzA$8w zd!>~I>3*BaC&i^kWH}LF6-&)ofK}5k zF2m~pP^KESu?zrLgVcvjvH>-$xoJRF?I&{)fXH_EEa73gezBe9%>{fNNMp-& zVK5w?OOj;FGOIYLQ_*Oy`_vr^^lc1zIHwwDk zUKNreIiD{CCT9?8;-lAS@q|s;hD^iPwhcEcX)H0Pbs;ryEWX}S+8GR)U>fLCwWKZ@ zzsXm(8Nh8crbehw%Kjlf)rl7YIWe$+!wfh~7g9g}rf`1{) z5uOGyAzy8yRb~en|9z2EW+f?XYksC}7j&Mf&fN1Zy z)8@y?dpo+lfbA`-$orN2jB2R30w^#fk=dfL{kL72^tj1uy$E02)pwtl{5=y&Ewxv- zEecyA_BYypWI`&pVN!#n-Q=5ei(gByeD4d;_opfLD*3lS8kN*5srIaOIb2e#g zg50aqvm%W6C~+Q3&P9z+QxPxV}`93ObSWCHa$vD1nGw#!6vnl*jdj zKosyd+86dCi<4THAtwG(jT&R+P9c2DQ6K3S(Mx|q3tiIBD%S$@(MkQrAJ|0}`XkgU zJD^`8I|}CXd7V&Xp=Pon=#m$gIEE3FWu0tZ!y%li7x9*W^t>CBH6d3Zy@@q( z^NE5l=A=duqcI1m*CD|9MQ5O(AT^6M&LwcA5o|A#aYKp?HD(WhdS)N9J=PNd^-NU* z>XiXXI_7Ka?5vJdOD864I`RjksVxyc1J=v2uzPqPr>-`ze&4MpZGiPy_#6fnEp&mL zgaGTsqy}M3K9owM0rhM{W+~beA$z_l#Zs>^4e>~8AiI(r@SeA!_F~v+1c?v8dwg_` zh9hwtE|71Ma}Yq88OT3^T&4k#&j&de59A*;V25~v`f?;L2B06+;s9lzDw*imF{lp} z4q%o&z@jFf3WXt1KdR+rvw@I4p@Z@l*SVtDL3`p>;MH>0YqL2=zCnHbK>er@2uT+T z&PO>c(n%+dq?fEwtb<~%3>vC=^p>@O^B#fvG7W5;GgB4}4ziT~Od|sLO@B^WWxY;> z{g;*xBC!Ac?F%!d>6_8KO#&!cZ#=yDJ}>egF9%r(83X>NmmMh-OztFK7@00*3#@rr zf|U&!fOnQU9KXKw^vM7)C;1%601+8L&WsFj3)p8Eupbpwx|%WaEaKXg0YuYUX8_rLq@`yYNEPyNH9{jnS0e;j}O z$;Tgk`te79G4;{BJ?_}f{eSvq^1L^1PafIs<8PKKh^JlqRXlkCDbr1U`|(BYd)#mT zt0c1=iEvX}UoV~HUg*`nTt>+i%ning{km`nr;tpEf=BLm>Rr4C8;4QJj zNo-3v3I|vov`g59oJfC)G0XIY%&hS2AGTs5tDspOOnCJqf zw7zH9uUsO!8^F}3yziHS1^Rt4apUA?CfH8KJI(l93Y_<<*b+aui1O59=>)3A5;gk< z+eZaHi`2uI3B7gTEUf`e+*hOl(4@$z1#$>9R%eZ*-bw=EzLHvacR#7_L-8MdVmxC=m*NkbSDW5Wwqac!Pdkq_vx#mzu(mq)mI3k}OMGzwZN{B;Bk&HY@ zM)l3SjiDmM2BRnjMcY1CCqkS+B<`gmrU9ck3SSgSZQdAQh(lNEkRKCwEx~|MG8;xN zTfiu(A%RhH*8)amF#%HBM(bN}0dR>#CdDo}|Fa$|p>xxjoP`u7em`+@9X_oy%l|nB zkYWfRWdJFj46Dg>buIk`{dFWO&Sj+0KH>{MVKH=|lg1=|Od1 zOCEI2cct^A{j&S{=Z~NK-8bKS`_116zZ?HPwExxN%Cq~$!$-gR>s}KANJpV@eO;!E}iuIM-PATPj=@Qf9lk3bDO{T zXXjn}zrOfa=boL_tvCPuoTU8o_$QBJ^L+2$%v1mVoWJsq|GYmk&$Y+qx%W-;Jo_v2 zv@F-kpMU=Hamw@k+4FnybgUcq`A^c957Mj@xas)$``>$h_3=?!4xH5aJpakZA04H3 zFx}a8MDWr{3rwA0+#l@*LB}I2q5}~N- z($oEslTJsG6CA6?%T#qNt?iGzbRrE%uIQK3oSGkMWu#GPdRR%PLy}s6^k}!QCJR)g zsUfLR-{?&X?S9$pFr9ShP*trj4pJ{DIu)(vfZ@^9@P6tBll`55th(_w z`J4V(C+L*bQu2BKtm_1tAKo+ho*R_V)8M`ehI?+184DwLdXLefHLY})rt_oGnHz{N zMPKrh{>3PC5QvZcNSfV$I!aH)Mh3c<{yGrNMVgMbO~L8@MVO9N%Fq{&Jgp8Nx|E*w zGkqW1B6WhB=&(N=ZK-19rp3SsmeQhA3i9ab=+0a`z34c>GV*e8-F^GP;n8rkD&6W+ zWIF0OK{H(lCcVSqsG+^R5b@h?&`KAB$u8E*)KolW77gkDowO1Z-6gI30vVGgNK3(@ zX-#GXQ>sfvi`vFTDJ`Bl!I9QHDpB)}6C7%V<+M1PY5H$8I^L+>otX{Gl-ZVyxO~C* zOMRN#A63%g&}52XHO=n_GsOj&$YexOjKoOa5}C&)Q}nCG8{W2cy|vfd>xI2RHKKati>Sb8+EY8*dgZmFwryEa)!U%X+Bwkw&3uzY<&$dnjs6r>R#eZlsk1k--yu3=5Ua-WfPt%MZb!k^_mcV6Reoe28pXWw5hi$(#46I~Pvdwg}}bU8(r(EhBfM zV$rdd=xpxP+jnk-jQw&{v99e>S*%y3C(I7>9-GmEHE|F(MC_@U-n61cYvLl#mZ_(x z^P=g}9jYnpC8@c>>y}v(Fv^RjDS7`4ZI_A`O&cXjIo6S>xkQ7uW62vT>gA}Y%{0~a ztwudSwKOyH%TYEfq&}mfKAEI=T8WB^$rOGy%1hQlas7Ny49l#IgQ6j91upDWEkv zSN9SA@+28pl&0ECvAsfSTh|-sNxYa@);pLKp_3F_jx~_<3F#A_iF0*Ce!3Am$uw@V zC`CK)5GMYHB@<~;az$89avFN5^^uNSCOxPm(*a2-a85d2?W@TG&19M!kc|7L z##7auX;WvW%xN;QY%BblOuROt!(_sT$E_OcgT%86r=r!!uxV;=KVfNZEU+|9GjAua zsME^AhpuIp)%a~p^6(+EL-T`s|{Z>V^a1)H#7S7>6Hx{4F#zzOR$VZAOa znc5KrtXJmk;#@F^mly}?Su{ux8hoVzoL{$-ux`68b z8?$N5XF)aLIT|LaF^elnv28NNpqk`&Eqhj;%!#Qa%HeAgWVU}4grsw(Xij&s|0VQ zIhqo92FcWnc)t{E8qw2>RY-EUUk=uxDTrlLv!!57FqE1u?gAXc{=OGvPuR`-rQmUz zUku4VQA;;C6|s37{kBKn3C=LtlZOD#opgNhL9ylrzv%Ieo8kiX^&qPy!SdkS&Bf@EhYBjw9UiU{!VwT~l zj*GEFM*SJt z@&oCHAoUO}PM4T_**Jt?QwC(!(6&)kQRU&`u6Qd{Zltx;hLQ`oSfxis+DNOKr_}{* zm3IhftMi;vq}f&cFr9(v<%I}{Dq?1g_Z!AkDwXyqwjY4Ml9nRdZ!*PGS4)VZxIa(J z5s9f*N;9_s3q(-?S+uMjo+Til@SMuD>@HYa?4h>o|DIc&`fWFhZrXme;#RD;&=g?6 z#H;%4nu@Dl$=WocI>3>)eXne-gF=AEre;glS~T`6Rf^n&8~m#0Sx?xLt0n8P8c0^k zxz;J-%5OZfFG(Hij2ygs29FA+jN7$xxlU75C@?c_*DEE@^{gAaRH?VurL1GEMAHqs zW=-s}P?1*9;Ndi_-1XZnW!nZc;#vy=Ri58&8pK8D@?oseLps!qKxWHV>Szf5Vd!gN z{_-tsnts;}9wCEaR^~Ecn7V1ZWiYBXFA8(73M~0G)2h#d4QS2aPgaZaie=#nO^8hF zW)d2hSuIC-Rg9F`jg=AMG$W-f=(U%$U=u66F;+&z2+6RDSy45j5~x*gF9N3cwy(s*-a={uqyo8A6OCgn17EAj4J5e{FC}@`!qc0WI&EWTWGl#;fWH7<5gJ)3$=^DO7uC@i*R;||SZ4xeYw<#o$q?}_ODrrMqJ<>il2M)I!8Lg)%6Nix zawbu#TZifl2vT!MsdW~bwk{=P)y*<8h*CnxuXt;omh%adu4E@){Nvr7YNk6q8=7v1 zv%L|(k#HnIEaEck*JjgXYt*#HW!_}wd$eq(Ur!4vtpZcUc4MZMb|g?$pXD>%Vu3d3 zAnXemve#1&i~)$W@M7F2*e0R8<+f zJ1qkn-i%zm3NqP>rx~cU(^M0!e<%d% zfs9(G*3kE*|L|m|ep?~l1Fj6F$6%T&_5BY81^XhDMx{n9o9eO$3{Qx}_T^Q8zfkn3 zUy&{XJ;hKUR{lz*HPO!*lP8>iv{yDKg?06zP2r&@2Z~M8##x$4XC;74i~2aaI2ABH zB53^Pai}@L^e2ka`Y_o7KP2@SJkR&O`kX}Izn{MT)}wob{+^6F8f=|}_n&+Vf;>Nj zDKmWDmV-{m*>Sd=9m0FtRa^?wwZKj%;2(kEMqsAMLmDsX7!lPR4a{g0Fg+b;_dL26 zYO+uAj6l5%C;U6w(KKCj()_WB;9n1!)?G0C9i973@>3^}F#n{VUJ!GYWWGOo5IWc_98DpbsOVBr_ifYbxSH$-vR>&-UJZmelOJ)UgwvrJJ1L%y8( za$2fd1=|X{p}Y8^ZuJt^s?YKot!mNQ#Hk8?)>IDV@YxpexJpRQ=UcNpSMYHX1x`p5 zv{HtY1w#aYlrf|e5?8BM2oD;CxFKbVs&`Aq-dv}ICw6oe)GZOiHKmlgT4YRr6@!g# zS@BV8^p~hH^KojfQp#J=)?h(O8HEs~3vz08b%}^+Gd1ah3!aMGqAjXIq7@-larm}? zdec+`t=t#7^&nm<%slv4tbOUX$KcLu!*!UL0rVI^Q=h1WGO;sbD%D}vRA)Ufn?22{ z>KIqs0-ube17#P!z_I+53Tr~$LUR<&R%hpsvN?aOYx`}A`ktIqHjUXlhOA{ILjdzs zc>Al5vI>R%pBO-w$2=>~$2jwqXC|i{hcksAz1~>zT*s-kJ50NYQ(E3wjHYYM61?4x zs#ev`UB|A{jWAl{ETw(jjT)Aj4-aXoq=QVDvptZ(^$3j?wSjiYqf3q^nMu={^^Tfy z49f|+N(O7L0G5dtVt?&RIN78p%wq`UU2I573|5l4*CyQcNa|*aKf>iFO3f_I$Tji2 z`KFDDY~Y~S19o~1%LrCT^rpxN4VnxdY`_KcOff?Yvx6J3n5U(xRnV^}HFSSBxYGhJ zrZ&jNOr-E6CZr2!!ZGzwSQacXm;^JmXnPzIiOU9SEYP`T(`X2bN$9Q1#Bof74~W|4 z6kw=GB55J5JLOrq3B7Sz28_HJaSQ%qP8J2y^$id)-x4OYOMh-pQ zRhEk!cGlI6HiZYCoQdSf!>cqgWcf>r`skjY3efNH$mAJoc3U4DG0vap5g_L9L)cdj z9Z!-1SMt^%e$3G-Uz4YvGE9;++}J*e{p9s~2klm)c@%e&sRIH}mfkvgkIDP{+YV>_ zFy7|GHgT(!ic=bwE7fKr^H|V>P%3L#b~0hk5>94K>*znkk&>o}gvJ9Ie2J?TeU0N2 zsh`HnnhZ%kB+@VM6@rf|n$~%oADSzG>l3E?qNHU>F!%c`IRqAqp2fmWzPKVfn(4OARVLTMMMpqcF4Og*RIW`6Za@q6^4S%+T%mGN zts2aXMyzIctG8wa_ndO=Ui5D@QZsm=3esHZ%GGK{)yhjkxZG%)YkSEYxbus9E|>96 zH2}!;f(zMW^=OZ&Z%Pt|hGI2}piNt~}1Wb~cX9Rsq@MOAdEpGMdsAo9IlJ z3i-nLxQ&8J=Hz*}aXhAjqBfB7O}azld;pyv$x6_IBO9)!l9Kc_crQa6-3N4?F0l z(XO*+?{dksYwtDod3(KI?M7j5`)&|yardy*iLS$FFFf9}+wyLE6Mo}S+GOmnd;FAR zZ`-~@dfdyRz25F!=Cyw9xfSDzv2X;fywym}>^iEDpHy%?$i<|oK(7#QV!0vJi{TR&*tjdOF}o1vvJwZ) zr7`4mPPt=ok6UP^7%0MWwv$)veB4+k@f{{NyK|sj;8ygIX$R}wNnQad8*!3k?=f+p z2?=1fvAP*hN{&g%8WK_rbiOp|MqvDKYzym zjc0eU{S|e6aQQbstTBhythgJ0QEd&ZU6uP diff --git a/qtlib/about_box.py b/qtlib/about_box.py index 99c3a059..7982cc6f 100644 --- a/qtlib/about_box.py +++ b/qtlib/about_box.py @@ -69,21 +69,6 @@ class AboutBox(QDialog): self.verticalLayout.addWidget(self.label_3) self.label_3.setText(tr("Licensed under GPLv3")) self.label = QLabel(self) - self.label_4 = QLabel(self) - self.label_4.setWordWrap(True) - self.label_4.setTextFormat(Qt.RichText) - self.label_4.setOpenExternalLinks(True) - self.label_4.setText(tr( - """Exchange icon - made by Jason Cho (used with permission). -
-Zoom In -Zoom Out -Zoomt Best Fit -Zoom Original - icons made by schollidesign - (licensed under GPL).""")) - self.verticalLayout.addWidget(self.label_4) font = QFont() font.setWeight(75) font.setBold(True) From 452d1604bd527afd1e0c31e58b9543386ded9515 Mon Sep 17 00:00:00 2001 From: glubsy Date: Sun, 6 Dec 2020 18:36:52 +0100 Subject: [PATCH 05/18] Make icon path relative * Removes the hardcoded path to the icon in the .desktop file * Allows themes to override the default application icon (icons are searched for in theme paths first) * Debian: create symbolic link in /usr/share/pixmaps that points to the icon file * Arch: the same thing is done by PKGBUILD maintainers downstream --- pkg/arch/dupeguru.json | 2 +- pkg/debian/Makefile | 1 + pkg/debian/dupeguru.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/arch/dupeguru.json b/pkg/arch/dupeguru.json index 0f83f7bd..9817da61 100644 --- a/pkg/arch/dupeguru.json +++ b/pkg/arch/dupeguru.json @@ -3,5 +3,5 @@ "longname": "dupeGuru", "execname": "dupeguru", "arch": "any", - "iconpath": "/usr/share/dupeguru/dgse_logo_128.png" + "iconpath": "dupeguru" } diff --git a/pkg/debian/Makefile b/pkg/debian/Makefile index 401c3bc9..5309f39e 100644 --- a/pkg/debian/Makefile +++ b/pkg/debian/Makefile @@ -8,4 +8,5 @@ all: chmod +x src/run.py cp -R src/ "$(CURDIR)/debian/{pkgname}/usr/share/{execname}" cp "$(CURDIR)/debian/{execname}.desktop" "$(CURDIR)/debian/{pkgname}/usr/share/applications" + ln -s "/usr/share/{execname}/dgse_logo_128.png" "$(CURDIR)/debian/{pkgname}/usr/pixmaps/{execname}.png" ln -s "/usr/share/{execname}/run.py" "$(CURDIR)/debian/{pkgname}/usr/bin/{execname}" diff --git a/pkg/debian/dupeguru.json b/pkg/debian/dupeguru.json index 0f83f7bd..9817da61 100644 --- a/pkg/debian/dupeguru.json +++ b/pkg/debian/dupeguru.json @@ -3,5 +3,5 @@ "longname": "dupeGuru", "execname": "dupeguru", "arch": "any", - "iconpath": "/usr/share/dupeguru/dgse_logo_128.png" + "iconpath": "dupeguru" } From 7f19647e4bd440140722017ad292adb660f77abf Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 00:56:25 +0100 Subject: [PATCH 06/18] Remove unused lines --- qt/tabbed_window.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qt/tabbed_window.py b/qt/tabbed_window.py index 193323ca..2912ddcb 100644 --- a/qt/tabbed_window.py +++ b/qt/tabbed_window.py @@ -230,8 +230,6 @@ class TabWindow(QMainWindow): # menu or shortcut. But this is useless if we don't have a button # set up to make a close request anyway. This check could be removed. return - # current_widget.close() # seems unnecessary - # self.tabWidget.widget(index).hide() self.removeTab(index) @pyqtSlot() From 07eba09ec2e451a66fb3174925639bf7b73da1fa Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 01:01:26 +0100 Subject: [PATCH 07/18] Fix error after merging branches --- qt/preferences_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/preferences_dialog.py b/qt/preferences_dialog.py index 70eeae36..c041b7d0 100644 --- a/qt/preferences_dialog.py +++ b/qt/preferences_dialog.py @@ -176,7 +176,7 @@ On MacOS, the tab bar will fill up the window's width instead.")) formlayout.addRow(tr("Reference foreground color:"), self.result_table_ref_foreground_color) self.result_table_ref_background_color = ColorPickerButton(self) - gridlayout.addRow(tr("Reference background color:"), + formlayout.addRow(tr("Reference background color:"), self.result_table_ref_background_color) self.result_table_delta_foreground_color = ColorPickerButton(self) formlayout.addRow(tr("Delta foreground color:"), From d53951752559c1232b51df91658a6c7b88354ae7 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 28 Dec 2020 20:59:01 -0600 Subject: [PATCH 08/18] Update Windows Requirements & CI - Merge windows requirements into requirements.txt and requirements-extra.txt - Update tox.ini to always use build.py - Update build.py to have module only option - Update tox.ini to text python 3.9 - Update .travis.yml to test 3.8 and 3.9 on newer Ubuntu LTS -Update .travis.yml to work with changes to windows tox (also update windows to 3.8) --- .travis.yml | 24 ++++++++---------------- Windows.md | 4 +--- build.py | 8 ++++++++ requirements-extra.txt | 1 + requirements.txt | 4 +++- tox.ini | 24 ++++-------------------- 6 files changed, 25 insertions(+), 40 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7cde9668..3d2b0fbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,6 @@ install: script: tox matrix: include: - - os: "linux" - dist: "xenial" - python: "3.8" - script: - - tox -e style - os: "linux" dist: "xenial" python: "3.6" @@ -17,18 +12,15 @@ matrix: dist: "xenial" python: "3.7" - os: "linux" - dist: "xenial" + dist: "focal" python: "3.8" + - os: "linux" + dist: "focal" + python: "3.9" - os: "windows" language: shell - python: "3.7" - env: "PATH=/c/python37:/c/python37/Scripts:$PATH" + python: "3.8" + env: "PATH=/c/python38:/c/python39/Scripts:$PATH" before_install: - - choco install python --version=3.7.6 - - choco install make - - cp /c/python37/python.exe /c/python37/python3.exe - before_script: - - pip3 install -r requirements-windows.txt - - python3 build.py - script: - - tox -e WINDOWS + - choco install python --version=3.8.6 + - cp /c/python38/python.exe /c/python38/python3.exe diff --git a/Windows.md b/Windows.md index 339ec10d..66d2b391 100644 --- a/Windows.md +++ b/Windows.md @@ -21,7 +21,7 @@ To build with a different python version 3.5 vs 3.7 or 32 bit vs 64 bit specify $ cd $ py -3.7 -m venv .\env $ .\env\Scripts\activate - $ pip install -r requirements.txt -r requirements-windows.txt + $ pip install -r requirements.txt $ python build.py $ python run.py @@ -37,8 +37,6 @@ Then the following execution of the makefile should work. Pass the correct valu $ make PYTHON='py -3.7' $ make run -NOTE: Install PyQt5 & cx-Freeze with requirements-windows.txt into the venv before running the packaging scripts in the section below. - ### Generate Windows Installer Packages You need to use the respective x86 or x64 version of python to build the 32 bit and 64 bit versions. The build scripts will automatically detect the python architecture for you. When using build.py make sure the resulting python works before continuing to package.py. NOTE: package.py looks for the 'makensis' executable in the default location for a 64 bit windows system. Run the following in the respective virtual environment. diff --git a/build.py b/build.py index 425f8fc8..a583f959 100644 --- a/build.py +++ b/build.py @@ -54,6 +54,12 @@ def parse_args(): dest="normpo", help="Normalize all PO files (do this before commit).", ) + parser.add_option( + "--modules", + action="store_true", + dest="modules", + help="Build the python modules.", + ) (options, args) = parser.parse_args() return options @@ -182,6 +188,8 @@ def main(): build_mergepot() elif options.normpo: build_normpo() + elif options.modules: + build_pe_modules() else: build_normal() diff --git a/requirements-extra.txt b/requirements-extra.txt index cf2b4ad8..1bf68f94 100644 --- a/requirements-extra.txt +++ b/requirements-extra.txt @@ -2,3 +2,4 @@ pytest>=5,<6 flake8 tox-travis black +pyinstaller>=3.4,<4.0; sys_platform == 'win32' \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 97fa8f44..e53b5793 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,6 @@ Send2Trash>=1.3.0 sphinx>=1.2.2 polib>=1.0.4 hsaudiotag3k>=1.1.3 -distro>=1.5.0 \ No newline at end of file +distro>=1.5.0 +PyQt5 >=5.4,<6.0; sys_platform == 'win32' +pywin32>=200; sys_platform == 'win32' \ No newline at end of file diff --git a/tox.ini b/tox.ini index fb929642..1323d36d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,35 +1,19 @@ [tox] -envlist = style,py36,py37,py38 +envlist = py36,py37,py38,py39 skipsdist = True skip_missing_interpreters = True [testenv] -whitelist_externals = - make setenv = PYTHON="{envpython}" commands = - make modules - py.test core hscommon + python build.py --modules + flake8 + {posargs:py.test core hscommon} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements-extra.txt -[testenv:style] -deps = - {[testenv]deps} - flake8 -commands = - flake8 - -[testenv:WINDOWS] -deps = - {[testenv]deps} - -r{toxinidir}/requirements-windows.txt -commands = - python build.py - python package.py - [flake8] exclude = .tox,env,build,cocoalib,cocoa,help,./qt/dg_rc.py,cocoa/run_template.py,./pkg max-line-length = 120 From c30c3400d41364ce93ddd84bb38f6f27a3af5661 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 28 Dec 2020 21:07:49 -0600 Subject: [PATCH 09/18] Fix typo in .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3d2b0fbb..22520be0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: - os: "windows" language: shell python: "3.8" - env: "PATH=/c/python38:/c/python39/Scripts:$PATH" + env: "PATH=/c/python38:/c/python38/Scripts:$PATH" before_install: - choco install python --version=3.8.6 - cp /c/python38/python.exe /c/python38/python3.exe From e822a67b3881888992da24ee653a1e43e2e3c4de Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 28 Dec 2020 21:18:16 -0600 Subject: [PATCH 10/18] Force correct python environment for tox on windows --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 22520be0..e8d96966 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,3 +24,4 @@ matrix: before_install: - choco install python --version=3.8.6 - cp /c/python38/python.exe /c/python38/python3.exe + script: tox -e py38 From 4b4cc04e879c650e956b16f985447ce77c9cd40f Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 05:35:30 +0100 Subject: [PATCH 11/18] Fix directories tests on Windows Regexes did not match properly because the separator for Windows is '\\' --- core/exclude.py | 31 +++++++++++++++++++++++-------- core/tests/directories_test.py | 8 +++++++- core/tests/exclude_test.py | 2 +- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/core/exclude.py b/core/exclude.py index eb8ffc08..557ac823 100644 --- a/core/exclude.py +++ b/core/exclude.py @@ -12,6 +12,7 @@ from os import sep import logging import functools from hscommon.util import FileOrPath +from hscommon.plat import ISWINDOWS import time default_regexes = [r"^thumbs\.db$", # Obsolete after WindowsXP @@ -19,10 +20,10 @@ default_regexes = [r"^thumbs\.db$", # Obsolete after WindowsXP r"^\.DS_Store$", # MacOS metadata r"^\.Trash\-.*", # Linux trash directories r"^\$Recycle\.Bin$", # Windows - r"^\..*", # Hidden files + r"^\..*", # Hidden files on Unix-like ] # These are too broad -forbidden_regexes = [r".*", r"\/.*", r".*\/.*", r".*\..*"] +forbidden_regexes = [r".*", r"\/.*", r".*\/.*", r".*\\\\.*", r".*\..*"] def timer(func): @@ -168,10 +169,16 @@ class ExcludeList(Markable): def build_compiled_caches(self, union=False): if not union: - self._cached_compiled_files =\ - [x for x in self._excluded_compiled if sep not in x.pattern] - self._cached_compiled_paths =\ - [x for x in self._excluded_compiled if sep in x.pattern] + if not ISWINDOWS: + self._cached_compiled_files =\ + [x for x in self._excluded_compiled if not has_sep(x.pattern)] + self._cached_compiled_paths =\ + [x for x in self._excluded_compiled if has_sep(x.pattern)] + else: + self._cached_compiled_files =\ + [x for x in self._excluded_compiled if not has_sep(x.pattern)] + self._cached_compiled_paths =\ + [x for x in self._excluded_compiled if has_sep(x.pattern)] return marked_count = [x for marked, x in self if marked] # If there is no item, the compiled Pattern will be '' and match everything! @@ -184,13 +191,13 @@ class ExcludeList(Markable): # the same regardless of whether the client asked for union or not self._cached_compiled_union_all =\ (re.compile('|'.join(marked_count)),) - files_marked = [x for x in marked_count if sep not in x] + files_marked = [x for x in marked_count if not has_sep(x)] if not files_marked: self._cached_compiled_union_files = tuple() else: self._cached_compiled_union_files =\ (re.compile('|'.join(files_marked)),) - paths_marked = [x for x in marked_count if sep in x] + paths_marked = [x for x in marked_count if has_sep(x)] if not paths_marked: self._cached_compiled_union_paths = tuple() else: @@ -488,3 +495,11 @@ def ordered_keys(_dict): list_of_items.sort(key=lambda x: x[1].get("index")) for item in list_of_items: yield item[0] + + +if ISWINDOWS: + def has_sep(x): + return '\\' + sep in x +else: + def has_sep(x): + return sep in x diff --git a/core/tests/directories_test.py b/core/tests/directories_test.py index 061e1476..8a5ddcdb 100644 --- a/core/tests/directories_test.py +++ b/core/tests/directories_test.py @@ -12,6 +12,7 @@ import shutil from pytest import raises from hscommon.path import Path from hscommon.testutil import eq_ +from hscommon.plat import ISWINDOWS from ..fs import File from ..directories import ( @@ -428,7 +429,10 @@ files: {self.d._exclude_list.compiled_files} all: {self.d._exclude_list.compiled assert "unwanted_subdirfile.gif" not in files assert "unwanted_subdarfile.png" not in files - regex3 = r".*Recycle\.Bin\/.*unwanted.*subdirfile.*" + if ISWINDOWS: + regex3 = r".*Recycle\.Bin\\.*unwanted.*subdirfile.*" + else: + regex3 = r".*Recycle\.Bin\/.*unwanted.*subdirfile.*" self.d._exclude_list.rename(regex2, regex3) assert self.d._exclude_list.error(regex3) is None # print(f"get_folders(): {[x for x in self.d.get_folders()]}") @@ -516,6 +520,8 @@ files: {self.d._exclude_list.compiled_files} all: {self.d._exclude_list.compiled self.d.set_state(p1["foobar"][".hidden_dir"], DirectoryState.Normal) # The files should still be filtered files = self.get_files_and_expect_num_result(1) + eq_(len(self.d._exclude_list.compiled_paths), 0) + eq_(len(self.d._exclude_list.compiled_files), 1) assert ".hidden_file.txt" not in files assert ".hidden_subfile.png" not in files assert "foobar.jpg" in files diff --git a/core/tests/exclude_test.py b/core/tests/exclude_test.py index 3745ac21..8bfc8cc7 100644 --- a/core/tests/exclude_test.py +++ b/core/tests/exclude_test.py @@ -245,7 +245,7 @@ class TestCaseCompiledList(): assert expr.pattern in exprs def test_compiled_files(self): - # test is separator is indeed checked properly to yield the output + # is path separator checked properly to yield the output regex1 = r"test/one/sub" self.e_separate.add(regex1) self.e_separate.mark(regex1) From e533a396fbb0cd7856db432973c068b4d6c67415 Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 05:39:26 +0100 Subject: [PATCH 12/18] Remove redundant check --- core/exclude.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/core/exclude.py b/core/exclude.py index 557ac823..29b00e6b 100644 --- a/core/exclude.py +++ b/core/exclude.py @@ -169,16 +169,10 @@ class ExcludeList(Markable): def build_compiled_caches(self, union=False): if not union: - if not ISWINDOWS: - self._cached_compiled_files =\ - [x for x in self._excluded_compiled if not has_sep(x.pattern)] - self._cached_compiled_paths =\ - [x for x in self._excluded_compiled if has_sep(x.pattern)] - else: - self._cached_compiled_files =\ - [x for x in self._excluded_compiled if not has_sep(x.pattern)] - self._cached_compiled_paths =\ - [x for x in self._excluded_compiled if has_sep(x.pattern)] + self._cached_compiled_files =\ + [x for x in self._excluded_compiled if not has_sep(x.pattern)] + self._cached_compiled_paths =\ + [x for x in self._excluded_compiled if has_sep(x.pattern)] return marked_count = [x for marked, x in self if marked] # If there is no item, the compiled Pattern will be '' and match everything! From c8cfa954d598f52b9b0a773b59772a3bb2fc57a1 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 28 Dec 2020 22:51:09 -0600 Subject: [PATCH 13/18] Minor packaging cleanups - Fix issue with newline in pkg/debian/source/format - Update pyinstaller requirement to support python 3.8/3.9 --- pkg/debian/source/format | 2 +- requirements-extra.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/debian/source/format b/pkg/debian/source/format index 89ae9db8..9f674278 100644 --- a/pkg/debian/source/format +++ b/pkg/debian/source/format @@ -1 +1 @@ -3.0 (native) +3.0 (native) \ No newline at end of file diff --git a/requirements-extra.txt b/requirements-extra.txt index 1bf68f94..c7eedf21 100644 --- a/requirements-extra.txt +++ b/requirements-extra.txt @@ -2,4 +2,4 @@ pytest>=5,<6 flake8 tox-travis black -pyinstaller>=3.4,<4.0; sys_platform == 'win32' \ No newline at end of file +pyinstaller>=4.0,<5.0; sys_platform == 'win32' \ No newline at end of file From f0d3dec517a2c3db81b6c9344ddf89b80d6ab385 Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 16:07:55 +0100 Subject: [PATCH 14/18] Fix exclude tests --- core/tests/exclude_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/tests/exclude_test.py b/core/tests/exclude_test.py index 8bfc8cc7..de5e46c5 100644 --- a/core/tests/exclude_test.py +++ b/core/tests/exclude_test.py @@ -11,6 +11,7 @@ from xml.etree import ElementTree as ET # from pytest import raises from hscommon.testutil import eq_ +from hscommon.plat import ISWINDOWS from .base import DupeGuru from ..exclude import ExcludeList, ExcludeDict, default_regexes, AlreadyThereException @@ -246,7 +247,10 @@ class TestCaseCompiledList(): def test_compiled_files(self): # is path separator checked properly to yield the output - regex1 = r"test/one/sub" + if ISWINDOWS: + regex1 = r"test\\one\\sub" + else: + regex1 = r"test/one/sub" self.e_separate.add(regex1) self.e_separate.mark(regex1) self.e_union.add(regex1) From b5f59d27c92cbaac41d2c421aab9295cf3321f57 Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 16:31:03 +0100 Subject: [PATCH 15/18] Brighten up validation color Dark green lacks contrast against black foreground font --- qt/exclude_list_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/exclude_list_dialog.py b/qt/exclude_list_dialog.py index 3c23b83f..a4aa4af4 100644 --- a/qt/exclude_list_dialog.py +++ b/qt/exclude_list_dialog.py @@ -132,7 +132,7 @@ class ExcludeListDialog(QDialog): match = compiled.match(input_text) if match: self._input_styled = True - self.inputLine.setStyleSheet("background-color: rgb(10, 120, 10);") + self.inputLine.setStyleSheet("background-color: rgb(10, 220, 10);") else: self.reset_input_style() From b76e86686a75897db912bfd4d79e46824bc874e0 Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 16:41:34 +0100 Subject: [PATCH 16/18] Tweak green color on exclude table --- qt/exclude_list_dialog.py | 2 +- qt/exclude_list_table.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qt/exclude_list_dialog.py b/qt/exclude_list_dialog.py index a4aa4af4..dd7d2002 100644 --- a/qt/exclude_list_dialog.py +++ b/qt/exclude_list_dialog.py @@ -132,7 +132,7 @@ class ExcludeListDialog(QDialog): match = compiled.match(input_text) if match: self._input_styled = True - self.inputLine.setStyleSheet("background-color: rgb(10, 220, 10);") + self.inputLine.setStyleSheet("background-color: rgb(10, 200, 10);") else: self.reset_input_style() diff --git a/qt/exclude_list_table.py b/qt/exclude_list_table.py index 7c6a3b63..3521c7a1 100644 --- a/qt/exclude_list_table.py +++ b/qt/exclude_list_table.py @@ -41,7 +41,7 @@ class ExcludeListTable(Table): return QFont(self.view.font()) elif role == Qt.BackgroundRole and column.name == "regex": if row.highlight: - return QColor(10, 120, 10) # green + return QColor(10, 200, 10) # green elif role == Qt.EditRole: if column.name == "regex": return row.data[column.name] From 39d353d073b03e06b003c23bc52b53d6c104c484 Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 18:28:30 +0100 Subject: [PATCH 17/18] Add comment about Win7 bug * For some reason the table view doesn't update properly after the test string button is clicked nor when the input field is edited * The table rows only get repainted the rows properly after receiving a mouse click event * This doesn't happen on Linux --- qt/exclude_list_dialog.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qt/exclude_list_dialog.py b/qt/exclude_list_dialog.py index dd7d2002..f5ecf5d3 100644 --- a/qt/exclude_list_dialog.py +++ b/qt/exclude_list_dialog.py @@ -118,6 +118,8 @@ class ExcludeListDialog(QDialog): return # if at least one row matched, we know whether table is highlighted or not self._row_matched = self.model.test_string(input_text) + # FIXME There is a bug on Windows (7) where the table rows don't get + # repainted until the table receives a mouse click event. self.tableView.update() input_regex = self.inputLine.text() @@ -160,5 +162,6 @@ Example: if you want to filter out .PNG files from the "My Pictures" directory o .*My\\sPictures\\\\.*\\.png

\ You can test the regular expression with the test string feature by pasting a fake path in it:
\ C:\\\\User\\My Pictures\\test.png

-Matching regular expressions will be highlighted.

+Matching regular expressions will be highlighted.
\ +If there is at least one highlight, the path tested will be ignored during scans.

\ Directories and files starting with a period '.' are filtered out by default.

""")) From a93bd3aeee4f05718bca7ab4f70f5aa797216996 Mon Sep 17 00:00:00 2001 From: glubsy Date: Tue, 29 Dec 2020 18:52:22 +0100 Subject: [PATCH 18/18] Add missing translation hooks --- qt/exclude_list_dialog.py | 4 ++-- qt/exclude_list_table.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/qt/exclude_list_dialog.py b/qt/exclude_list_dialog.py index f5ecf5d3..425d4eba 100644 --- a/qt/exclude_list_dialog.py +++ b/qt/exclude_list_dialog.py @@ -80,9 +80,9 @@ class ExcludeListDialog(QDialog): gridlayout.addWidget(self.testLine, 6, 0) layout.addLayout(gridlayout) - self.inputLine.setPlaceholderText("Type a python regular expression here...") + self.inputLine.setPlaceholderText(tr("Type a python regular expression here...")) self.inputLine.setFocus() - self.testLine.setPlaceholderText("Type a file system path or filename here...") + self.testLine.setPlaceholderText(tr("Type a file system path or filename here...")) self.testLine.setClearButtonEnabled(True) # --- model --> view diff --git a/qt/exclude_list_table.py b/qt/exclude_list_table.py index 3521c7a1..b58e2579 100644 --- a/qt/exclude_list_table.py +++ b/qt/exclude_list_table.py @@ -7,6 +7,8 @@ from PyQt5.QtGui import QFont, QFontMetrics, QIcon, QColor from qtlib.column import Column from qtlib.table import Table +from hscommon.trans import trget +tr = trget("ui") class ExcludeListTable(Table): @@ -31,7 +33,7 @@ class ExcludeListTable(Table): if role == Qt.CheckStateRole and row.markable: return Qt.Checked if row.marked else Qt.Unchecked if role == Qt.ToolTipRole and not row.markable: - return "Compilation error: " + row.get_cell_value("error") + return tr("Compilation error: ") + row.get_cell_value("error") if role == Qt.DecorationRole and not row.markable: return QIcon.fromTheme("dialog-error", QIcon(":/error")) return None