From 63c224cb53739c5b4b5630e3a0bfb8208d785879 Mon Sep 17 00:00:00 2001 From: f321x Date: Fri, 2 May 2025 12:49:47 +0200 Subject: [PATCH] add qr reading from file to ScanQRTextEdit and SendTab There was no ability to read qr codes contained in image files. This could lead to confusion in some contexts, as `on_file_input()` of ScanQRTextEdit will read the whole content of the file (instead of looking for qr codes). The revealer plugin for example generates png files containing qr codes and uses the `ScanQRTextEdit` to get user input, for the user it would seem logical to click on 'Read from file' to load the generated file, however this will result in the wrong data being loaded. Having the option to explicitly load a QR from file makes this clear. Also it seems useful, especially considering reading QR from screenshots doesn't work on wayland. --- electrum/gui/icons/qr_file.png | Bin 0 -> 29382 bytes electrum/gui/qt/paytoedit.py | 7 ++++ electrum/gui/qt/qrreader/__init__.py | 37 ++++++++++++++++----- electrum/gui/qt/qrtextedit.py | 13 ++++++-- electrum/gui/qt/send_tab.py | 1 + electrum/gui/qt/util.py | 48 +++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 electrum/gui/icons/qr_file.png diff --git a/electrum/gui/icons/qr_file.png b/electrum/gui/icons/qr_file.png new file mode 100644 index 0000000000000000000000000000000000000000..efacb739ac6795d5c8050ee82aada827fc6fbc95 GIT binary patch literal 29382 zcmb?hQ+sAj*Nko3wmoqswryJz+qP{d6Whtewr%r{^W`~s|G{^#ak6`_?yGvO>RJ_{ zASVtFg98Hu1OzWBA*uuf1pM;~3Q1Ht#7BV`DovafClyg; zfS8pPUEQ29hvRnxc^Me?E5Vp$(AfKQWtWnpBPylR8fh6qxy(plK=xzXqJnrUW78{$ zEt36B81)M?)7yf|DcFaW*fc$~C#XAuNK5mtjSL0>H(Ww0-8z+7QE3ydI3lt`GJHUh z#xT1Gfo-rP$N3bd--EKKc#cC$va;X~!(y9Aj;^A*%$(JmI}`Mrx!#3YI^8El0$=_5 zZ`uOS{rc~K)`vBZ(mhLKpq||=#@>%Nd5$FWF6UO}jG%>oHa z+Qu(ya2>kir zxjDQ2^7R(f{d{ZRQ9rmNXCcjDNAW2Tm^V*_-`kuoZrgNL_>l~dU-okZWBgX56t-lB zR2C{*7(RgQIppm<8Tq=aFY6tws9%EEW&8j)d4|04y;1c#e`lK!feJ`wmAG3G!)@Rt zfgYCoI}(KGED)4q7K+jLHuL0%s@cdN{c&?(zOe+F{bhY&ZlU?Uaqdy)Az2hN-0%HF zkh*~70X!J?5qzRCO??tvnTii3+6$&BXr_^LIdFIdodKo~i2@x9Xp zgZ%pXw0}`nXWaOvn_OBNGit+7R|*1k7DX9#&jrS$Mm$}&Ug9n9T>*^GV)riBzx7CX z*z(BvRo}rb8MhMp66PEjZjc79IN!cyfSS{L8hBx*GMW$mI+(o*15~+c_nJ?SLcy>< z0+Eon_(SOQlNIrR<^;L4owRYTdEHnOzOMScvwpvE^1IX`&a>iaUjTbQdF?!=ruDq6 z%TLvQb5vG=lA0G;CTA&O z$p?>_+zrKyVSc;$J&d?92RIL?;rOff7~S+UzGct6arb^DU|vF-+_7{nYU+V2WfsNU zGZQ+;@ZBJQwZ&q?kJxxEK9gg)b&2;{1{L+hF4*tEu0s{S9A0<1SZ{@5-KP-V=3QchidRc(H9CUfl;4-(7W8+G`=> z$6z=vF$5nj%7Em@9sV^+y0mUBmlbRveRysk9BqVDAY@l(y_>83bAZ78wER~Wug~;G zbPX#|9N+zXTJfv0yObz=I5Z8?*vDPn3ku)W0!M);7+btKs|l|<&x}Mk6-i`AeVa!P zC;uB%%^xb*?C6LjS!jkKZ=bhwbv^)L*Ej9XQ`q&Rhdph}G!WnGkbSSpa0HEu?g02C zD`3+Z`r;WmD+o7O@EV2qpLwADS5rVICPElgz~lshZ#-x3GxDODD06iwtn#12-zLuq z&+Y2R-=|f*r>^$B3nx7>t305)4d32;2M9vANI^thFT;$YzmU;y;Sh*93~)iTM-FE= zAs*!JGa4^%=(wipNBnr>jOz%$$@~cWyOX)Ak!nRp*-Ofo?`}uI!v~A98S*lKz$!$FJpP$-@V>rK7Kuc;R^R?y=3LN- zGL>*~m!@t?4c+`zTj#Si@<|BjK7F-sUybtgGhgIa%V7Ntcml}3y?2N^u71e{6=csL>W#epxd3(WM#yU69h>w?bp0N8 zRom{ZMhzZsp^#G}cc(Z;NRttjoHxShiz%N5oyoXuxA#kqn1SM62!?=;JPr*_g?@jl zsPn#t?A>M6`&vfb8Zl@3nchojW-6~UjT_~))pQV|P;U};hAcsUa@Xof2%^s3Ro)#n zSQaUEHS4-Ge3aO}l5+%=uKOYC_4gdWr{8+p(J<|St#29IKPjcTPIW&A5+SP}(bZP| zA^*mKq)jmPvYCGS*L2W~k7MF7JIS{AUCDR{vdcKOkLR8L0`2+c+s@e^SitD?P|dp>)u|3xa2kDw^K= zJA)r~5L-|ET>rIpfM7Twme)S_3XmG{J>#Y%T& z%wuflyooymS~Eid*eUb7>3uuMt7iovz5MyKHI}z8g$V|j&YGcOx44|Tr)@fD1tjmg zMc!;YdlT3&kTW0}MU_}TPp6a9cQ>o&+4iQJp0X3%{SDh+D0oKwdu#8ojXvmAEtYr} z7b2lctzl zMM=|*q9`e6+6MsN?RDRm=3U4?|90S#Bi(p{>)XnN#sUfYN0O<(McWD>#m$Z=JB(yJ;1$_D)Uu@rn&76(5pvaSg{!rX}{*pPtT?j_6 zId=XB_`FFK@TvTzTa7F69#>50A;41vjc?#M$Gk^Ymol^_+Sb=G^n zb#{MD2qUIPib{vz!wN-4GP%?6y0UufYxQ2*$aSP1xMBsO40w#;UR8z3ro09d-%Sp? zYy@=|td@vMu)7^k;~%Ob@byoJb0wX~uKxWjNJ{VVeE4{7`rYaG=40>sn;tSZ7sB8c z$j+zqVUAyJsl_^sH!^^dD24TWoUF-$^F%hTCtIE0n^4(|D)J(jEI|yym3;K;alQEr z^6SM*?*ZKHjmHl2zd^#HMr>Ilt1Nen;Tt*A{jCRvkS!%xEmWZI(dxXOK)a5V^@Swp zU&s@MmBO$jsFOM=DFFgyuRQ{A!nVtB&#uK=KhXw$N(?>O`R5)%=~m;qV>nqA$6 zaJ^*}cCd{)YH`p;wizOM(DgA4xLaQ7j_&?IlRXcI$#HEHflH8-S-|9TI3o1>OzZl} zY5kMb0{Ig}D7JyJzx_##KHjwRvc91Ofo=4^PtiC|?TJ#-&obRqzlUn7=_r9`P472c zybsQT_;M!2kB%U$Pt(gJt2D*`{q4&Sj9UZ1y<*q;G@#ZKW8 zwmfgCwEDIlkZlGY@Oy3Y$8Aj^zky!7^Aambff5SF6Za^0WHNezr(+kI`{~d#Dxfcd zSTV(9K<0A(WboGtiA>b+Hc=iF6p)_LO;*W0Tl<%5kDZ(W z1BaP8gXekMlFv3{Gt<9uZFs4ym65hq_fAr6TGXV^Py~0$mLen0dFZc~Gv2?LanpA% zlT1Q0;_b{jaN{nr^xho>KE?7rD>-u~LUzbkg?~0Ez6d+eNjn%Uu|GDqsi^pzkDCf8 z_euY4_DJC%^-@u(9+U`iV&~-1Z;xB82|~|{ve{xv%b;1)u(9OXYhnh`oG@{Ly#T*g z(>y>#o-aa~lez&ceBU&1n=w}V;R(z{lWk|>2B#ZEQtAkyA0DLB^ocPDUveq4k%=Q2 z8rskuD|CjO5R5U;KFed zyTTT0?oxz@WxivofDpdjqPjG(*@DtVGX2F}!#5t3Mk6%>i}s(mHz(a?XjA-hB_5o+r8NlS4Iqt?Wkf^qZ&<#;U|m;>>P|IH6$<~u*50sbPzl)ieYUph9#NO0kVj2$d_CWx3AB#*vkwSH&%4jAiyV8h z8N!46690`r22)W+Lv9oe8U43gAHVNU{kwkxPQ(BJ2hsK!Di^yB{vUk5R!koeaaw7R zn0Qox-uglA1by5U<`Y591W<7R)82{S>&h$O`_9e%wq3pL`SN{rBP(%=qbZ^xrcsMf zdzFf&m&7zx(N%@?;hLIE#$#`HIh=Y!~yH-H;$RPTbkLyMUNPYK@1EQQ)^n%`lVsXA~JrIBeq-4}9 z3Um!ALR7j7far(d_YNwcbJ|vj*5GDfN0e53`@(nk5=Kb+aeN@s?O)x5jV3Ccb+^QN z11_NkKD6B8e_jOU_E)D;kS$&}o{QW|P-KWMXgXYx7_#sl83&i>!LvZq`K(ke&+yqVheRga2E4%j#r*F)T*QF#} z4H@z96jImZ^mP~oSa;qz;$Z1@!-0Hf5Eli+I%KN!goi7Vn-rcyhAq;?UpCTkonIT> z?VqbWh`YCSzG0mNJ|Kd>=f3t`f@lJQ@6q({dKeFf8GZc^Bj&N`@)^Ju^m_4kZdnv{ zBCmH0>kcr)0t<4HstIWQAs0#eLHfa5g}R0#Os@y;^&_

2qUvj!j80g9JJH_f~j?5GE|diDTDoqBtmfcvk3koGmjzmuf)r_8LII z=!0|xAcWroCwF&@ov@^1I^&v4-Q z4;*r(Phc+x<7yJ{COHzXF2Wr)-{?}`B-orFILUW#6@AT%|DAB`A^ ziXmNEmYiG%)rhivXncPjL1O729pQ6ziR)s-%pFNCq&XkWxdh`BqYTO}7~+xnR7g$g z!6*}sdo2idJ(R<u|DmlE*O5Z^^pog&FvNHWeL4nMN1}M5qez|#CX^ulRX{$q;|3^L zR=9Ft0yFBo%_6!)8;C8nMRiqX-3TLGxk~}fxqUSh7F9t3Ospbd8Ja%j*8oR$` zFLC7_Hl`rZZ7+Kjh78U`x~>)&!NLokfwoYex?TLspG;&3l%yi)a|hIp-~tlD{@!6H zHFo49&_$-PVu;<7>oy~%M?hPIg@H@Zps8CYY%Zruu#*fr3Cg?semXW2s8tyoS+M60 zFayrv`qvk{8xV<}C!j)2=boqLF(ZCv6sQKc_@MXL^Oc^9p&xJrtEecCsXcoc9HB>^ z#>&Royj04bhx|95Z-1^Kcf_X?b)1`qtxyddr6KXOe$RB<7+gS^hvx;)vp)q0VdapT zddlrvV|b#A?_kquxIcFiLrYVbu{{BQ{7+X|z1PI;r&CAVU8eJn;IgKNsMt@rc1Mty z4O(~uwl}5+Z<0hh7@eu>?VfhMiP59WjWmZ6Q<7F?PP?CUYyW%D)G~rlaC#nvQ9(a5 zn7PwT-)=)|v^L@SNqVga&l0JvXu%GuEIQCH@nTp=gc+d#*MdAt6o0aG;8^WR<%v)> z+nNi=O}#XKu)9Y4+1_}e0o|_Bh#Dz*=PUI}pgKttE>WSiK`p;1MmnF52%&t}m?8Fe zscSwiNlFfvj8eweDVZq$MQevyMR^6C7DBvJyd3f;QqLb7aj7EBgv0(OlcsBQ_l3z5 z2pBqJZhq_zQ|LmJ7u+n8j${@iE9%kUzT;=(a&V9BIp6EqI(M3G5X@$ghS&$yW z=n2%ilCEf*0e*Oknqj!{p{Y6+x`Xx!A>O7BK;ISya)9jBYek@xv5dI z#MdP4k6Khk85ULO9DDEGe|U{v=>q$xFixXkaRQWNBo3vEOc_G0fgIPnljDa?2Yr7e zW_+~(=15e=2GTI&9lyvZD7{)&*H6DDgACajDis+=2i_*YS{r9mn&gb2pH&flA^{JL z9J#yVWYNF1L_gPT)B{^k6y{9HX^bXa_|!kV0;6B%M!~F26r^A~&3^^R^wz&^pQQLJ z^8>AaRu(V`wxV*~D+`T4!s#E(Oy04-h8#jip8!sx`}sGw3X+JV>bDncy${50;Pq4b z!aOCmO7S23d7g)qu$3i9E2|mgQxDLvob9`WySYmfb^=90jXMR1m+?T)u15`6EI7ZF zj^}IV2iGe-wJ-J}BxJ<$lmP9PDiVCF^kg6-!y`}2a_S)L-{)UL(jP-!j6(RWQvVbo zDz+U$u@}IdfYK1ek&V#90}2#2Q^%%y%1{4@ecsr79W4LInY(`qME558)RApe)Oo08 zI#-0WJg49ha|q###DM<*ciIJ-fYSa4RDIC$C3$n&XOH)*( ztW7^fKS=xamAIE4P+=3V0M{-4^KdmoSMi+rC#P_=oD_vthExgD+oyP;x!&rF9GKr; z(W40A#O(|n#)4#D=G^1+@Qr~3#HHgdD_Snv&uY9`vD{rg{cJ)6On2qHqgn(_)J{xK zsypF~EhK`)=O!ejeL43B#|e3oO-y$T3<|#YbBn^SI6qV(yrWHy;@c8f1fkmfn`}kL?-Gw6p%BH zM}Vg~x2dE=nZMCS=}?lCAKS*Pq7>;%?lOe9=XoK{$Aph}1e#Y;4KXD^8x}z0vb`45 z!|p(si3(Johj%C+_aiSiy%#giR|F+@8cL3&Hsi(Uy6s-%m8^v~aX*dj`1p(z(`vk0d?BJ4l@%vzQ>))4L< z0!}AaRVsbcjDIR(+{WvamTo6SaOiG;-P~M*{H0lNb$Byv-fU2!7_CYGm#<3kbi@8q z5OkaQ{&u$p=53ccuJ)fznrxbu0L*QEBGJth{VzbT>^@B5^Ta%8rLeuoXoPz2A^&wR zM@WE}`-@^KF(;`M#X9``2OvWtupKz5=#PWU_D=mOx|kv4O1O5Pk79Rrlc7;t%*@zx z%=gt#@f_{Aa78MkJc4U{__xI)q3(%d0=zMZiCXHLK>^zU6lcwCrpX&tFRpl0qWLPV zefZZ@vGCT)#WP5Z)ffXEFgo{O@3+p3@vu%+Jd>4%7q_|QGb^bWE1!zI^Frar!2Hfozz_1DBx zlDSTrUnU{%j7&n+Q!_MQ2VEOdqnIMNklTjt)6%sC$(K?OC7Ah~90g(hg!2>+iK zK-M_kTqIJ(M>C5|X#iBOk+h_i$ne|*-ol|cOGLXj5^lTYC}<6d5j{#1UAh0)0fy9P zkBLsHJ4`vwK4;&?5il~Fn}${X)R0aSu}oyzf~&4BbMDRL=15rTjA0D=6`wyZ_)XGO zQSaG(_kvMS(B8cEOJZi@I6v(r7wA;_pL7{R9#e$!KrcFSz8)#``(}fZgyjEI?$KqduN)KGQx1{oW3Ww<$E07woL5o0>Jx`psuv zew6aMgQ(AQ`(*0nskFvbEl)@EB_MY?89|ehe>-EUb0cZ4_j)V{dde2u)himw$ll(4 zPq%d@n$#y`6r+bYJ8x-Uko+&sy~1hD!TeVFOV)IvGBCzWTyi2>Shm3c-!Jn!di@8E zgJygw(^i_GRM;AI9v!7I9f>fnA&5x(Uzio2*XK!I-Zn?& zxDA$QvD+>Ru6*?A*H?}^&bjijfZd8en<=?D7Ui_UwFLgCu2KdFvd+8vD?y=4 z6E~R|XJ|i16ot0-s%9Ye9F@v#1TN~ZK^0$Vw8(q5}sZhLIe|_lSI~YJnXe6-L~fvY{pu{ zSrSX+MY{d34Sf|{ki(pINS+waBkiZN<&RfY?3kq-oC0$3-j-b1Nu)N)t@=+(R|D)< zJ`S6mHo0CejHId9g>+0{A*4Z7gRym)L1B1PiN*+TKJhFOhQ)p{RF(uG8a;uuR0Njx zW(Wg!$3HfV1kn0OBk0;=t5v$f`Pua9%7MY%`oG7DThKPfkdZ(2+AVELe}Ej` zv}pDXLbY7!@tS&^yumKEFIVw|i6L-vOR2PcOPAW5$hBU&plHq`

I=2V8`>YhrjJk6Lef*=SBO%d#5k&u6BcyYE|sA(kEQv zb%8aVS6j0rCe6R?K0CspN2bb`BY!JY0gntyq;3o5yopIGdm$Q@Q$6wpcn95lKG#uS zwrS0rpb;c>6kd}-f6U5%v3yVLyhF1E{Y&&?imbXaQNc?Pd{!!FtCp%XZ14p?)ca|( zd;66)T@{MbEunR!2GnFKFuJ}AQ)H%K=LNgdF(XgKTe}$2L1>$9NXZO2a!wu&Q153c z!XM?h2-qS25LqkQ)UPyN4+_xp_}U<0rT`26c$YxT&^N_l5{^YKdapfnq1ZW-9m1LS zd#%tgd9Q-$o{9)8SrsWJ#fu#;^R2!yfe|&OOgdBgvdm;oivLoH@zth_>#$JN z7c;X5l~z)DuR9)@fAxH%|0W&r`;rOsTmPW|eSh}b9yqkC9Pn~Ct+>uvjB+4;*)X^k z5}pWN$ebS1}|6#u*3WCcpN7|`+`fSwhycL;#eO&#Eswvk^ zi=HjQ4Y=|$@`g1p`+;GVuz%~w$$I*OYDIs&la!KiA7ozpRO2p8j=EH>-@?IyeG8tr zQe0~b{vtuZ@7cl}@}(z2Gruu0#Ss|pF&)J$6u#wk5Xxhvw0s``v(%#=53Lg~q@2T$ zG?tgefvh;7?&e?>aXRT9PVKX>888V|S!9&mu!D7A`vlbD@L`_^JX`tZQD*;?@6P*2 zF(jazG^I(T_m8f2pPiKE>-`iu%VfVXVKikp?Oa*-6()Y~YOh#5&xbFjihtO#|V zq+x*AU;_xfrf`MpY;=;t)#y|-pQ14TsF_YG#A5#O_e@QMfdkAjMIma+8iOD#%Qo!ldZYkL1eW zBB?{1>?Fl==kiM2&se&cO0Np1&6~8mGZHBdLVz@3>3WFbwY|AC=y`F#Q z;Jpb?XEeZdOLawDfXer{mUA5)XQ>(bXV(H~ic z$VWocL<1_yJt3FloNBiD<5BV))Ei4eMEWei)GS>T1llH0(u1#2eLXtuVJ_QaW`pqP z*Nr>AN5-pOt(VV~YKi`hmm=tSuCS4%jgYMb&Tj@6&7Ht%exK%C{Ytr>qFhU|gskw5 zKq9eCUN6hyJ)S|;v%o~1jCs}6!XPsctww0rU5ZCBseMrCHQG5T+M9ytH=XkKZU#X{ zy+HMLXR-NgMqdE#Yf&C2xjAEu`o%A^n64$%zoBMyX60_G$mRAbN+2Yeqzq9;(HEqJ z9n*MRLBEFr%1p0#snok@A$u%dw8GjqLJ~m{CxmLWApa=#-GL*K%=J6ha;hyy&yEwQ zf0tf7T?+1i26xDo)_J{(|>e0i~ar%#}lQ~Z%JF?@-69n#$z-d zjr-X8G>l&`rX_dA&JN9|i@O?3dDvb$YJEGI`p_Y;jIb}OWFrkJDk+{5rvQVMpA=9_ zbib_=4IzR7t3zs5c%3Szax|elDupRz_+5vx)jwXM{{HpeH(K=YEF{g|l+d+G38q`h zW)0mmi#`YWZ>?|jY;+Q2Nlnu&#D+W- z@WLcUV$yZ?ZfZK;KpjGJ;mG91FCuiCft8%QrC9K#$f|>tZ}C=C(j1KNJ)ZQx3R(!U zZg!$#k-3QrhBOsMbkCY9&@RN7?S=|X-ET$Xz60Y1+HAa55BmE`UY{!h?Q(#*@E|kx zPeSw*>ku7?e|NTyC^wF#oS|o9`&{r{K{E`Rr=4)PU~JFw&{^X<9J@!7jWuJH((`yc zf;!bu(ERZXLce=)MOxAcm!Rz-U-7~s=x4$I!tkmte#yT@FCMfQ)jj?+7c~>*lZ30#3or(;$vuZ(Ifvh&SZGaOP)_} z5K=gOzC(oC(J%e!Pv1N#~>heiGSaX1Zv z3o$$&o7Z&svOA(vrYRz4N~0bdmD3G#gl728wkLIbZS7{OJ!`oLNtz&X!7K7Q>CLvN zpP9iLA? zSoeL-c$oW7QQL;^o1d0iv~USq3yrKW$?KLYgozvlS!%@K%yO!kZoP5^?cem%ssRG# zLEE&Vy&^yucp90SXOi~!`H+*D`R}}#^$zsDAEILT7`Gty+ZK8WvJPRAlydckD_9xd zEoe6s-rE!Zev2FXG&q}5f^DdSIB=ROM0L|rXz{mSo%s?aTUU6P3EaZ`wv^SGZkioY zevep+rfA`pn?_^L_~wl`F8(ehH#LRPV=*H~lzXW9sk+#P^Lo-#_{9 z4?hZO)qGIlKTTP8WxZdT262WaffypCgDXVk$p@XS7IQs60I7pB|Drw&68DaB_C$;dEy(!=Q7KS34b!f*zwTI#{k1cG^6qOch(XBbA z`Z4otA08v7glThrw9W$IEqxfKyUOeOW=hju%e{+gr#&{^>U@sJ>2e#A#=v-l9r$WEzVQf^XaV06c+QcY(fK8s zrqq%7dL;KKMiE8!NK=2|Yio(iUC06Qk~!GB+xvsNs}AQuj?$!cuud2q492N2PZ^

T&mQHbKk*0GdbW37ZUWbxc6;h$T=a~k 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 Sequence[QrCodeResult]: """Might raise exception: MissingQrDetectionLib.""" qr_reader = get_qr_reader() - image_y800 = image.convertToFormat(QImage.Format.Format_Grayscale8) - res = qr_reader.read_qr_code( - image_y800.constBits().__int__(), - image_y800.sizeInBytes(), - image_y800.bytesPerLine(), - image_y800.width(), - image_y800.height(), - ) + + for attempt in range(4): + image_y800 = image.convertToFormat(QImage.Format.Format_Grayscale8) + res = qr_reader.read_qr_code( + image_y800.constBits().__int__(), + image_y800.sizeInBytes(), + image_y800.bytesPerLine(), + image_y800.width(), + image_y800.height(), + ) + if res: + break + # zbar doesn't like qr codes that are too large in relation to the whole image + image = _reduce_qr_code_density(image) return res +def _reduce_qr_code_density(image: QImage) -> QImage: + """ Reduces the size of the qr code relative to the whole image. """ + new_image = QImage(image.width(), image.height(), QImage.Format.Format_RGB32) + new_image.fill(QColor(255, 255, 255)) # Fill white + + painter = QPainter(new_image) + source_rect = QRect(0, 0, image.width(), image.height()) + target_rect = QRect(0, 0, int(image.width() * 0.75), int(image.height() * 0.75)) + painter.drawImage(target_rect, image, source_rect) + painter.end() + + return new_image def find_system_cameras() -> Mapping[str, str]: """Returns a camera_description -> camera_path map.""" diff --git a/electrum/gui/qt/qrtextedit.py b/electrum/gui/qt/qrtextedit.py index 54514d548..80bc6bbac 100644 --- a/electrum/gui/qt/qrtextedit.py +++ b/electrum/gui/qt/qrtextedit.py @@ -47,6 +47,13 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin): show_error=self.show_error, setText=setText, ) + self.on_qr_from_file_input_btn = partial( + self.input_qr_from_file, + allow_multi=allow_multi, + config=config, + show_error=self.show_error, + setText=setText, + ) self.on_input_file = partial( self.input_file, config=config, @@ -62,7 +69,8 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin): self.add_menu_button( options=[ ("picture_in_picture.png", _("Read QR code from screen"), self.on_qr_from_screenshot_input_btn), - ("file.png", _("Read file"), self.on_input_file), + ("qr_file.png", _("Read QR code from file"), self.on_qr_from_file_input_btn), + ("file.png", _("Read text from file"), self.on_input_file), ], ) self.add_qr_input_from_camera_button(config=config, show_error=self.show_error, allow_multi=allow_multi, setText=setText) @@ -72,7 +80,8 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin): m.addSeparator() m.addAction(get_icon_camera(), _("Read QR code with camera"), self.on_qr_from_camera_input_btn) m.addAction(read_QIcon("picture_in_picture.png"), _("Read QR code from screen"), self.on_qr_from_screenshot_input_btn) - m.addAction(read_QIcon("file.png"), _("Read file"), self.on_input_file) + m.addAction(read_QIcon("qr_file.png"), _("Read QR code from file"), self.on_qr_from_file_input_btn) + m.addAction(read_QIcon("file.png"), _("Read text from file"), self.on_input_file) m.exec(e.globalPos()) diff --git a/electrum/gui/qt/send_tab.py b/electrum/gui/qt/send_tab.py index 81c9ca3fd..2577dc8d7 100644 --- a/electrum/gui/qt/send_tab.py +++ b/electrum/gui/qt/send_tab.py @@ -180,6 +180,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger): menu.addAction(get_icon_camera(), _("Read QR code with camera"), self.payto_e.on_qr_from_camera_input_btn) menu.addAction(read_QIcon("picture_in_picture.png"), _("Read QR code from screen"), self.payto_e.on_qr_from_screenshot_input_btn) + menu.addAction(read_QIcon("qr_file.png"), _("Read QR code from file"), self.payto_e.on_qr_from_file_input_btn) menu.addAction(read_QIcon("file.png"), _("Read invoice from file"), self.payto_e.on_input_file) self.paytomany_menu = menu.addToggle(_("&Pay to many"), self.toggle_paytomany) menu.addSeparator() diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index 0b11435e9..5f33d4f32 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -832,6 +832,8 @@ class GenericInputHandler: fileName = getOpenFileName( parent=None, title='select file', + # trying to open non-text things like pdfs makes electrum freeze + filter="Text files (*.txt *.csv);;All files (*)", config=config, ) if not fileName: @@ -852,6 +854,52 @@ class GenericInputHandler: except Exception as e: show_error(_('Invalid payment identifier in file') + ':\n' + repr(e)) + def input_qr_from_file( + self, + *, + allow_multi: bool = False, + config: 'SimpleConfig', + show_error: Callable[[str], None], + setText: Callable[[str], None] = None, + ): + from .qrreader import scan_qr_from_image + if setText is None: + setText = self.setText + + file_name = getOpenFileName( + parent=None, + title=_("Select image file"), + config=config, + filter="Image files (*.png *.jpg *.jpeg *.bmp);;", + ) + if not file_name: + return + image = QImage(file_name) + if image.isNull(): + show_error(_("Failed to open image file.")) + return + try: + scan_result: Sequence[QrCodeResult] = scan_qr_from_image(image) + except MissingQrDetectionLib as e: + show_error(_("Unable to scan image.") + "\n" + repr(e)) + return + if len(scan_result) < 1: + show_error(_("No QR code was found in the image.")) + return + if len(scan_result) > 1 and not allow_multi: + show_error(_("More than one QR code was found in the image.")) + return + + if len(scan_result) > 1: + result_text = "\n".join([r.data for r in scan_result]) + else: + result_text = scan_result[0].data + + try: + setText(result_text) + except Exception as e: + show_error(_("Couldn't set result") + ':\n' + repr(e)) + def input_paste_from_clipboard( self, *,