From 6888b55612dd9eb8443e298dc4d9c219612246f3 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Sun, 16 Feb 2025 21:07:26 -0700 Subject: [PATCH] Fixed On-Screen OCR region Overlay --- .../data_collector.cpython-312.pyc | Bin 11112 -> 10299 bytes Modules/data_collector.py | 186 ++++++++---------- .../Flyff/Resources/bars_template.png | Bin ...lyff_character_status_node.cpython-312.pyc | Bin 5005 -> 5251 bytes Nodes/Flyff/flyff_character_status_node.py | 53 +++-- 5 files changed, 108 insertions(+), 131 deletions(-) rename bars_template.png => Nodes/Flyff/Resources/bars_template.png (100%) diff --git a/Modules/__pycache__/data_collector.cpython-312.pyc b/Modules/__pycache__/data_collector.cpython-312.pyc index bb082f31f103164178cd6209590e8da85690ec89..2cfb3154669bcf66305291adf2b72ceea8d6040a 100644 GIT binary patch literal 10299 zcmd5ieQXm~o-_8?9)H`h$#;SiAYdTS1VSNDXn+K=1xg@5Yd~JdGf5o$5oRVpT&I_= zIIW{nC0tn%1Qo7YUBj_bvwPKDR`FYC_wk$ee((1^{=MC9A)x&0&woDf-xUP$C-k5c!&2j!k0gkj1Vb=n zlnBT*86feqE}+A;KA^`n6)@o17%<}66fi-pi_$T3z#Ov#EHP`qN+KNks4Zp>*kg`> z13yzyXUr9F#oPgRtSnF#^8`FGZ@`Pk4AE7w@<4g4B2W>l3{=Ld0#(p&WKBa3WwTlx ztXA5LDNw`Efm+5KsADXFdL6-f?k^1nR!4|{k0C=1jP)uJXk=`RovmSO`zgk8l?<$5 zoY1?LaRFS%xB)gXWdPUfh(iS9xk@lz)^T4iBMWR8CH<=&qlaUDy=3T)g$7v3jH^Rj zs86!uW>+LCu$*5f*?KyLhog~jNQfljlBwrRgc)E3zd4DCUDVvi!MT zmgiY66c(CaJ#gZjpge71udt6lfjvL|5dt&)R^gpHUFYR*3 zqxCB-Ww#_TO2YjalCM~NP&(uM2*>h1VK5~43NOkR;eC;KM2LibO}(23wJgy%?`#fGZ9MB?DmK zgnPs@bo6hUyD)5Hk z8=*OY(X{)L1|LYrhHrG6eG=(ECJ~_as6N0U(Ol?q5EK?LvMdmO6NeNs<`~JTvQ{vL z)fyE|gfB=Yd3(HM9Esx&C%jC4m#Q1h@|<9Vk;p3&IVzEtC328QR6}CxARP{ru7V~;oRjRN-q_8lD z&p-ndEG>1py?+ejT?RDv3h8!J|_uS7rmP7gV^0L+p-jjPC?6PAo2`{T-8N zTD^?+PWPg2 z?qwL;5C1$u1%y#FY1a3`oHtAF9q-6fh94h#>(F0!KYwDRv+#)-p!`k3{Jr@~Gvoy( z+YH!N6onHNEjw2D2J4E+@TklufsO}>^PjHZxRSiC?IcBFj+G%emDR+Uu7Gb#zgKq- z_+U&wMvWQ93N&&tM+50?_%|eB=g=~ux}O4WSd!c^li*g)m@=uow4k~jLtbTEt5bBL zi!x>|*m=qGr$~(gV1sNav-Z415d*dw>Z^uIQbGX7UoMOh+fP%5lZpsfMYZ#j3`;^|$J0LbLAKjydvYopUX77k|+< z-`tU{+MoWxVx@m(<3eRyx_hyrF;~$lR(5<&8*3+%Jj`t&RFKlwmr0Mf3lkJ)_>hdnC+963Cq;BYtFBl z39IvY3y3T_)>Z!jjhjf%7TQPXLxmv~ej6iGWHH+ay@)MV6h@Z>{4k~lAld|ZtY4FA zT}n4pMAtF;tAzS?eM(;X;c-?}>(DeN1R?a?NuxvSkmrU7;wy7aH=U zK0v{j0T?I1i{bz7#ms^EwY%m>0Qk!Mv}6JSzQl6i>0vh#YRm25{@CK8G-g~F6%kVO>9?xG{2-6+4+)PfI!IzT1cLC- zp927*V|7iwH1X23XTe%GelTx$PoAAPJH7Vq##t|&?6?rrsfxf zI%OLWKB6F!!a*dMecXm1^1T8R2b4w5+=`;z&?w2CF&d4yNbLb!h^ARB!l&m}}9AXnOR^Ub@2RxjK#F@n9u4rh0=g_4|o-WAmun@8H z^3Y+F8t}?(NIZ=4uf}^5!?>5~O(dd{4X|_wVDkDP?vg6cbOd|3P@IpBgxR>TKNP+& zz$HfFj8vM3KUdo-*0z3fBG-0AgumJ&|H6HG z`FEGa+S6J344zjd(p7gFbJcBPb=#}}`HN+}yFhQuwPop!M>VU{RF=d+I&@G zn!4ur60#ny=9$pU$ywcO%j}xj6LaJ&Ki4?dGk0L#@iJOS+a}Eu=69^Jeo=G^9E3m= zz$zdPDxfebf!u)NXX>=vh31(vpnZVp(OM|#Tx6LL&J~5xD>Fd6irTl-@)@f5Xo%1o zK?GFc(MA*zfKjK`tF)BS0u`kjDyAS*^;Z0BSTU%+wSrw!kq}Y9=opA_G25If^lVpA z1PCj2Og5aofQ#z^0O>b$Iu2<#4xL1zu^3|e1}&6$r9l`jql7K@I+}%oNjz&mH^L7} zPRKt%a)n(I4&HCzE}|I*7KxH%8eU4`>W5JdSC*ia*$fMwgk>*Ty;2mku}grLzYYLo zp{#m(`@37xrbS2DjUCes(>)pef@kf5V_jOGr>&E=3ENc9HD{i7PC6zWQ+$@Ld155Y zt^%nM?j~;~Z>MH@KR$c!?EUjudUwH$JLMTN(~=1h!uTl(+BBupW}|2ANoy%s2Egg^>tt?$uwK1*4n^3K^>D|rgGV}pC%Xfkem%Z# zEmq*sQ9!@R>g@_VBv-Gz(-k~(6(%huLh??}WO5?;c51r!?%7*sZ=X-Mq)FsxgJXZk z`r+9HTgxJPx^dz9)=bNXZ@j=#GrRso)CJo<$0BZh=)}Ug;fr?#Y>6? z`qiS4TTqo%F~|DC1`vUupeh`=x+1Q#f=f|BAgGWsItQ5HGQY)@hFz6VGzuo3yw(H> zRwAhG0P^|-g)kW4=k2|BPTW0z>-=Zdjf-_n86mfRx43@y!upr~P`CHGd5WCck@r;H+;@Fn zrtPC$@9+9x_e0Ot{HD!Q2eaM{fAm&Evb}!a6AR&6cX#yG=dgtyY)^)8$21!+y~on@L??xPl{`ghN@5I#|HC;;-*f5PTSgy2{WMtm zBOsikNH8Iw{4Fk!X**GxTBRugqa|z^jFym$0ZQ8k%q+4`l$>KI6qy#B7j>>E3WsA4 zs%FI?JZj^kD15Aaeg^l)@8qZ=t4c{N=8^A^mEwCTCBqo23Dy+})Q}WuL1F>LO8AtR zVnryg-3)NZ4lJMl&u$tSHV8*$4;P)_F|zVr}FeV zhk`CG7b}N*EvOHD;DK*<_`3!+fD#o;Tw?vSW(GAsNHWCP%fZWXH7HkCo!V0^jQNt<$$(n=yQBzh}Si6zg6TD|b#AN)FZCwcoPe zcA{BY(^EO!`Zw(vZh9nN&~$loE}!V~Wme4?W=3Y&hps(&_o|$Go#7XsYkZ+r0{cl>O}-|w93{pZuaIQ>trJrI7A{AKc2srgf{=T7}lJoUqcQ+@NTVMzH> zcOADJqHFz(QFLuB%=prvB<-Jumk6u6e?yDIl*R(@y+L3G8 zDK_m~XlfU|?di_%8U}pb6Wvqn^r@_E&A0D-=iIsn-rrRJvieuGzZE`x<9BcTer&$= z{I~8Df|Dn1!cqUcn*(ekyE#5+pkF+Je+VtP;3w5T9woT9pdUVKYF?jCWA|lWINg{< zPe8ULw3JCnHWcOJ!YL>|T1vpGSq7AKct}^`jl*XONEpQ-3j_%xOE4J2zu^H>{I%9Lph#1EM|> zgCCaY;=__Ye5sW?ggW)dyN__lK;n-%a^PL?ldQ_LLnDzD0X>KO0?Dp^848ADjATF9+0}9CNN?~=_rc!7 zl0$hs+}VBTa4+{)Xo>Ae$NtVE!KJRP=v`O|q68xTl>`380sfGUKibOQe7evmrY0hy zOro{WlIT71&F$s>79J6Q@pk|`(UTsmsied~K?=P6m*eyG6!iUiVmGP=zRlGU~OW~}%b;6}B(zu3d zv^-}E)^cpHj3nt=&+x2axT7>uNz~yl1p^3nJc|S1zR{b z&~2OtXe;Lhx}7Tn+Ge7LD9(3@;>vl~EwegUuzi{iRNTXxqXDyG8IFg?dBsjHLqhnp z;w0IF(U`;w0h3}IIT7W?c}a1N^iNL4qLHu^O(p^s#WM0LACVO6$gyNJAt|O2%paMO z_=|8IjmDujqp%~#!cfZ#3NvzyPe9FqWGpE_=Yz?FG_&(S-^pV_a$E?<{V*dho*d;x zkr%=dsq2*kubz~&yKep>fA1q$^S!r_ut>_0{2Iv~AQ5fnAx|;z!{>eY_du>u5>Pcw zMy#X}gDih!W&$pksF@k@~TUv5188*DsXxX~!foC)bH{dG4yMIWbNdxdJ~ zB_OW?j8KPp05(c!K+=e%CczIEg~5Fn@hSa&VLO(%KsH0#i4Z!V2)Ql}0(p$%H*B)4 zX#>aI-gReL<~iBfurMJz19JmpkMc~;Io)$F3+*sc1b%wbg)0a}z+;{N6S%HT$>0x1 ze_*Y1#3-fUR9!9ZCXW*o9(Zsl79VJ=C6PlWY6R94) zSTZ@OSVZV21!#qqL|kB=TW(Tp99-cksM`fU5xEQuW4&3nT4t-){JU?Lud(~)`g4rs z?V&e^zB~LlClH3nY1PwSAt@PxaZtz@36wbqRE*~*0}jQZ>kUyc3sH`&RcM9@ zgcc;*khJ1_4iQuW=$$i3NwMc=GHN1FChW$ZNKh!n{{S*aW+1GxP zUh#FL2S{~`>}pwawcf31nEz(FKYcFKoEcfKX?ef#dgD@f*}L3-oBr|O?Vj7`e$ucCRRYISdVc)hA6TeU~7+Oz!J%HZp3RVUJiz9^V8=PrA9eE#=yolm`M zTQFaBEd*uH4t>@;uGaf?RC(j0M#^4t#WC;5JayUqWq@+JRdC54e0Xw-LWsZv`w$s% z4FR*T7RoSjw04AM=@ebex`9&M{7s27&A|9jrwhH&K&=}y#L$#76$2;Ee2LO$H>b=K zMZ{ri0O0gFv~fTgGDj8q@I8F;$22?2{X-OaWCur|9f0#<;!MZEKqnpVeFAm&{u_vZ zismLrl0r`w;y=4L7LK3h!uw{nJthX-d%;u+$HaY*Q5zkBLBuj3bJSALlK7)19Ea9%1LJw>lr>eenwI(lf*ixT#5I}8}n~0w7%bQy<_p*_3owC4?Aym-llKvTB+$<>dTHx%Ro$ zl0LX~da?hFx@=94T+_4Myjt`0s3v+|;cCdC{1Idd?W;HGXl8PNUO*g&S zBG68g^rIungA0b5#jw&(JxFT5`d8YXByJ5w>eZ9H+2ThsMbx&i~vrYDFyW`n@-$HrX)Vc8412*AX za1rphDXg|}0-CMD8%WS02Q39#7~_wFmIB3#B2@qwLKC(_attf*S>~umr4(DaVrvF} z<&l+Q%d@t6*;c=5Yh1B)tk<-?*OUgGRheb$WVY@O+wj23*xMetD7!n$*2rwlLi57! z-eGq>E?k=UJlqG9RWtisVn|!UHi6^71{R5HujBDZ@rBrX;lEP)spsb+aZ=T25_x zH+;W0|JTtO{@^QJTd+ig4rj9;;-0o)dhQ2BT*DAhdQFrbs7 zLVDna-0ABjclt`F!AS#;#SNCy)FcEv*ia~%h)SW5dj3i#1%P>B`Z%$P6n30X#(7DY zR-7U)4QV%}aC3s-(ZN`FTofFzI>mPo;v+nG-J)7`2;(i~YW}zso)%w8j3y^ZsSEn| zOJKL1hG>9NeWE`!DufeaY$^gcvp*a;J1!)r5}e}Xgz$JMIW`9E6}Je3M+?n3Y!iaivH#KK@shBS3Gr>d*du>6_I4XDtqu_X$1 z;#)v|g>F~LybZjrg-QqmtXDK=D|X2hyH+cDvK4)DMc-=0-n1jfx>VO}p>~aJC3h%b zH}+@i_sI2oK6*9Vdsv45`onAN5mJr9cB3U*+bh@hE=z0d-n(^8X=aVB&NXerYkjVz zEzR6vn^aLOloj2x21+G(zPBub3Soqtp!Lz zQ6no=&_cq~KvFT8(WDL}S3i>zy8op0GI!qk5-~@qi0KscI0;XRUK@B~04(T- zK;RR6zjsWRJq$H##kztO=swuvBg3;;x1Rabcl7yeDI4(^*;)InW7Y{=C?c;ZHf2va zQqEh5VUP&7bl*xXLn`62N#ZP=RnmQ~iCV4PxNhE~C+&cNgT?oj^us{Sri5$e%rmfO zoGrz?NBy~eDroBRn+zz?h>4~mX7H1l3jo+hiOnhu!7%qw{}R7SvGfbkaO@r;(2NU0 zqW)J&WUtR3a0>`T0`9fo2BNS8JBaZKIf%9zUZLs_TA~skR~3o%p_+hjhPYo3JRUnCtc5~w)yKoLn!PCwBh6!9t03e%Em z5je=8gJ=oORByxzqXxZrkp|HNuqdltc>100v~9h#?A>QTn~p4+SADIkrQ6fy9P7N| zns;SJF1vHA`%3A2X+~UQ>%PD!qvk@sFz~mhesb!kp|#2*KMDQVzHD9|&4>#FiG*lg&iUc=flPU>(0$Cgd|6k$?5c;w(2G5b z;WgI|D7*5?{3|yG7u%Qid@%Ll%*~nA`o1;S^H_0o{^*To7MmAGmdvYlJJ(!Y8%OB> zzGbO?IkNoPO8fJ7TzkKCQng+G_0UFD9i>Hd+5T>qZ(yf&gL+4JK|p%=I)N`{5pV&H z>hNMb*yd*v>C0^ZRa8hf6ClJzg>*=!0lcOu2o0f8Nz&#O{#J9pv-K*a=E7MuGY3Hm z)~G9s%`3+BqSX%u4iWA^nPLS4LrNx81I2NeACva01xE1t8JJ3qU|+->23Qq0yc$3^ zId5r*ik(kP!K)&O-6|}&IuhUugq0?fq7cJQaU`gW1@sDqvq18YB+Noyv4>-^kP2&v zM#3Bte8$bOD4S~9hzcE5oN6AH5|N&ON$z-ZO5~5hVnr308a4vcrt0s)jN&hWsNVSA z`MrO*@5Za|pSpf()!DHjBvoC1;{=w!7h0|Ef@l(kj`H!SUo^B`wP(zk(Og6GV%znT zR~;ET(~}7=cyqOl+1hTowtMN^a`W=YZF;q~FJoJ8+qG=_dE2vBkHRaHZ|5*B)d}FW zH2C50&EZ=w->G^ww`=zTvsS$`x2G4cyOiq2`*y1RxknDFyk^0>(7$kQu^FH<>+6zz zT}%9j(VNj*XYcs>a_u{RIQ{+UAAI|x*KhaVKKEBA<@Wx}z*>20uDb`Xo&Qo^`(+i? z*nZzhxjG*=pLAE_&p?xr*cI?*a}w!kssM*j=WR-wY=Qh!6PxPr)x3 z1^tSTON<|aFMv>AbJWy^z&DJm8*9eGGxVEd#cMbdVy^HZV1XGVSjP^duf_|MRScBR z+5p!WXuAc@>TTJr$DFlqJrx@fp=%W8Ge&H*W|08|@w{(s0_&G!^ z1}%y>;KJ%xaI$ExuR8Nzbw-P?<*TG))=_YJe%sT#XTvL_qKIW63unT!n{s@;b58m( zzsYI;>fHlbirDSTy4yZ= zx8+=Ag|Cp{t#;PgB0F0?XqTNmg$fuOx+^nMwyIl(fA_9stL*+pn%Uq7RMgBn(sa5f z9nN^kP))KE{yXpDblYV2w#9Rwx_9KNYrqqz_+i!etA0>({ghnYnXwcvYk1#w-S(ax ztBtI$dSTBW_bmzwQ#m8M?rF+;T4hh`nx`%2tIGO1WM9VzF>+Z?zI{+`@m~8bf8U8)Hmn&t@v$wsn z=lLhsY&;YBmEQMJrHzkUI;eYy*VPRf{N4=gXvnFF+@VB~MDBE5HBAY>1H}QeDpF|K z5~+hPJ<#JOQncYi4=<5}S0Pjv7W1OeR74aC8~g(dBx2|Tq(KXLX(6x;p|3*Z)BVW5 zhK?ITU$lXh96bY1Li~mT3&|)oS{gac12aAtfv={_Xv-LFj1k~_D&|RbMtdWtjXn;q z1Hn(Zhl~bZqVJ$M5A`1%I6N3SJ{%kjSXBP%7-qb%7Lg4Ef7IyZAHeO*Cc}S||MHK} zL>vVIZzWvbO!K>E7kjQIq|KK0|8y{x{2H-SV26M*AfS>`OaiUblrJK>kf_0L;jC>)I*>et1V36JuVvNuMTC)uu&f=)b|4D7 zSKYLI!e78Gf`a&`K<=Aqnto`dXzyo~^E1l+8CCWfqUAEvtTK$DBLoaLw5O>*q@E zZfjd=SuXjw@ge1)D<4$bXwQQ(6J7d%t)QzPbTjlb4_uXW*Q4EIH0{U?{Ssa@(dw@L E57 200 else 0) return thresh.filter(ImageFilter.MedianFilter(3)) -def _ensure_overlay(): - """ - Creates the overlay window if none exists. - If no QApplication instance is running yet, schedule the creation after - the main application event loop starts (to avoid "Must construct a QApplication first" errors). - """ - global overlay_window - if overlay_window is not None: - return +class OCRRegionWidget(QWidget): + def __init__(self, x, y, w, h, region_id): + super().__init__() - # If there's already a running QApplication, create overlay immediately. - if QApplication.instance() is not None: - overlay_window = OverlayCanvas() - overlay_window.show() - else: - # Schedule creation for when the app event loop is up. - def delayed_create(): - global overlay_window - if overlay_window is None: - overlay_window = OverlayCanvas() - overlay_window.show() - - QTimer.singleShot(0, delayed_create) - -class OverlayCanvas(QWidget): - def __init__(self, parent=None): - super().__init__(parent) - screen_geo = QApplication.primaryScreen().geometry() - self.setGeometry(screen_geo) - self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) + self.setGeometry(x, y, w, h) + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool) self.setAttribute(Qt.WA_TranslucentBackground, True) + self.setAttribute(Qt.WA_TransparentForMouseEvents, False) self.drag_offset = None self.selected_handle = None - self.selected_region_id = None + self.region_id = region_id + + print(f"OCR Region Widget Created at {x}, {y}, {w}, {h}") + + self.show() def paintEvent(self, event): painter = QPainter(self) pen = QPen(QColor(0, 0, 255)) - pen.setWidth(5) + pen.setWidth(3) painter.setPen(pen) - collector_mutex.lock() - region_copy = {rid: data['bbox'][:] for rid, data in regions.items()} - collector_mutex.unlock() + # Draw main rectangle + painter.drawRect(0, 0, self.width(), self.height()) - for rid, bbox in region_copy.items(): - x, y, w, h = bbox - painter.drawRect(x, y, w, h) - painter.setFont(QFont("Arial", 12, QFont.Bold)) - painter.setPen(QColor(0, 0, 255)) - painter.drawText(x, y - 5, f"OCR Region: {rid}") + # Draw resize handles + painter.setBrush(QColor(0, 0, 255)) + for handle in self._resize_handles(): + painter.drawRect(handle) + + def _resize_handles(self): + w, h = self.width(), self.height() + return [ + QRect(0, 0, HANDLE_SIZE, HANDLE_SIZE), # Top-left + QRect(w - HANDLE_SIZE, h - HANDLE_SIZE, HANDLE_SIZE, HANDLE_SIZE) # Bottom-right + ] def mousePressEvent(self, event): if event.button() == Qt.LeftButton: - collector_mutex.lock() - all_items = list(regions.items()) - collector_mutex.unlock() - - for rid, data in all_items: - x, y, w, h = data['bbox'] - handles = self._resize_handles(x, y, w, h) - for i, handle_rect in enumerate(handles): - if handle_rect.contains(event.pos()): - self.selected_handle = i - self.selected_region_id = rid - return - if QRect(x, y, w, h).contains(event.pos()): - self.drag_offset = event.pos() - QPoint(x, y) - self.selected_region_id = rid + for i, handle in enumerate(self._resize_handles()): + if handle.contains(event.pos()): + self.selected_handle = i return - def mouseMoveEvent(self, event): - if not self.selected_region_id: - return - collector_mutex.lock() - if self.selected_region_id not in regions: - collector_mutex.unlock() - return - bbox = regions[self.selected_region_id]['bbox'] - collector_mutex.unlock() + self.drag_offset = event.pos() - x, y, w, h = bbox + def mouseMoveEvent(self, event): if self.selected_handle is not None: - # resizing - if self.selected_handle == 0: # top-left - new_w = w + (x - event.x()) - new_h = h + (y - event.y()) - new_x = event.x() - new_y = event.y() - if new_w < 10: new_w = 10 - if new_h < 10: new_h = 10 - collector_mutex.lock() - if self.selected_region_id in regions: - regions[self.selected_region_id]['bbox'] = [new_x, new_y, new_w, new_h] - collector_mutex.unlock() - elif self.selected_handle == 1: # bottom-right - new_w = event.x() - x - new_h = event.y() - y - if new_w < 10: new_w = 10 - if new_h < 10: new_h = 10 - collector_mutex.lock() - if self.selected_region_id in regions: - regions[self.selected_region_id]['bbox'] = [x, y, new_w, new_h] - collector_mutex.unlock() + w, h = self.width(), self.height() + if self.selected_handle == 0: # Top-left + new_w = w + (self.x() - event.globalX()) + new_h = h + (self.y() - event.globalY()) + new_x = event.globalX() + new_y = event.globalY() + if new_w < 20: new_w = 20 + if new_h < 20: new_h = 20 + self.setGeometry(new_x, new_y, new_w, new_h) + elif self.selected_handle == 1: # Bottom-right + new_w = event.globalX() - self.x() + new_h = event.globalY() - self.y() + if new_w < 20: new_w = 20 + if new_h < 20: new_h = 20 + self.setGeometry(self.x(), self.y(), new_w, new_h) + + collector_mutex.lock() + if self.region_id in regions: + regions[self.region_id]['bbox'] = [self.x(), self.y(), self.width(), self.height()] + collector_mutex.unlock() + self.update() elif self.drag_offset: - # dragging - new_x = event.x() - self.drag_offset.x() - new_y = event.y() - self.drag_offset.y() + new_x = event.globalX() - self.drag_offset.x() + new_y = event.globalY() - self.drag_offset.y() + self.move(new_x, new_y) + collector_mutex.lock() - if self.selected_region_id in regions: - regions[self.selected_region_id]['bbox'][0] = new_x - regions[self.selected_region_id]['bbox'][1] = new_y + if self.region_id in regions: + regions[self.region_id]['bbox'] = [new_x, new_y, self.width(), self.height()] collector_mutex.unlock() - self.update() def mouseReleaseEvent(self, event): self.selected_handle = None self.drag_offset = None - self.selected_region_id = None - - def _resize_handles(self, x, y, w, h): - return [ - QRect(x - HANDLE_SIZE//2, y - HANDLE_SIZE//2, HANDLE_SIZE, HANDLE_SIZE), # top-left - QRect(x + w - HANDLE_SIZE//2, y + h - HANDLE_SIZE//2, HANDLE_SIZE, HANDLE_SIZE) # bottom-right - ] diff --git a/bars_template.png b/Nodes/Flyff/Resources/bars_template.png similarity index 100% rename from bars_template.png rename to Nodes/Flyff/Resources/bars_template.png diff --git a/Nodes/Flyff/__pycache__/flyff_character_status_node.cpython-312.pyc b/Nodes/Flyff/__pycache__/flyff_character_status_node.cpython-312.pyc index fa3bce4df7419ac6a8d5d8ad797194cd1380c04e..3192abdb659b64b895a81563a4a6881051ecb2b6 100644 GIT binary patch delta 1852 zcmZuxTSy#N7(QoqE@x+Fc6Gg=M$O>08_lYywb;})ST7AttKvh@gk{;8+BNI$_RNSE z##nj*11Ta0T1#8nJXH}IXbL4SKDG~q5Q(j2h*U}-p--vWzVxN%%sN>G2j;*3|2OlW z|NNgcei%`IQB@hxr{st4C%#)z%i-8cV|x?=9qM8VP5H4fp4PSLtRA-vQ!}l&HBIiodOvi;;nC!jfyqPo zC-*6L_ebtAT(qikl>pc{5^N9)c{UqNR(o=uPilGDlO}6DDbHzkU0(B~4VaT4p#HAY z$~StUd9Ih|)8;^=f_%;YRm<9zd`eppDRu;NI%{C;rzQu*V###Uip8)(n@k=iN7T? zyL?mLK^0r5Vh7c1p_*?xc4|AfYCE^li_g7fTc~Wc_*YbYKv6-0Be%I?d4&GyXTXM& zkX+*{$Zyi;DG!29fT*^50z^FmLB~L- z%`2Vfr-1l#Ab#2|`23j0So%zayL3RMJW@6B{<1%sRf5k#>YEPNprg5vYMt zazC&JTgi-aNjyit!CWRkEAfIyO5uZWWUX^sVUtx>akoi51DgjRC>(?)yA!0Ozm8uI zQ<6BkYwiy)7uy>L9}pbutvoh5(1zZGxVO?z?gd*2=c^(@D0H6tQu5B+^`6#KA00pL z3!n?+VyFm)$TNR2(j}XBP49(@s!mHny5APvh})r}#nU2WJzygmp?r00w&&DHaQ>34 z9X-AARLambdi#?ZJ(-}|oMvT?7NC}RH;&^8%fPu!Xln1^%oIC~=<@>e>PUP>TVy9R zYRp>Q5ed_?1h|ipentiu8D!)QMv56>cW9m;9O~8_*n$gO?K0IcTNLwrKNDHtywG>W z4XS@Df-t+Tjxu3bF&v+XSriV}Qxkp}(9MO1m?)CXf?6@k#Eaxf!7Kh&CN@(-x(d%n zcB$|_I7FbhW~V5+RTN#b?%Y_v@vx|82|YsUQf76Q?w`Gmm&nJ3_h1`&HQX1_SOvFu zGg%IoRJ2gtWTWvKc8jyE>8{Qo*$kK0o!xJ36vpAdBd|VL>%e3<|M delta 1657 zcma)6TTEO<7@pZZw>@VM%d$n>wzPwk%fdowu`v>XRxDy8$dek{WOLYafJ2u(o0+rH zC5PfIUJ@@M60npp{b1zCi?E>r9>MNOk+&?)Te6E_~3*89J(o+Xv!q#oB3zH z`M^{8MWG!?5v zGMd$o5z_fhscH3!)J-}koJI1oLz(LpGXpy5A!8OJMUS|}QrTxmy|ORrUY)Fd(T&OK zM(;|Nlq|{|MqHoFIn)6v@#h?vWII`%5MmW=!W^Fw+w!g4f>pF;9ooUj*kqQB!#4`w z=~pL-mCt+Bc8jxx3gqu0rwYs?OR<0tWNkvsTCD}Yo%nKb`ax_c-FyUx^Y$*f#*DAgd^(sDVs==!-FmH7|yIjryw ztpkM^X6lHx3putMLU28_@MSp^ti9`mm&wJY$=G^avCzPM*yHK8-YQe^o^Ye zp}?3otL=xit*pbviBheXrP?{X&uELvyJeIbnRpPdk%{k7M%g6;b3z$)w~SgNV+Ll8 zjJZb{ZI=wp31wnCGBdUYhZd`msSE08m!H@<5;(NFJ<244ge{+kqo)yDUKWvl{<$#H zdC6$S2LHs!^SbyT*7%F@G%v(ka3}vLeie7|r_3`-78Kc={9V(E-f2qT_}e^yvm+QK z$1oFrBX1miTv}@Y$UnHQaD`$>{D$}E`EX)H3eddD--xF8^~6DbJ=V`R7XPlkd1bm1I>T(io3;wXcNZKg+@j)@DOs?fWtB<=H*fjiKBRNnqmET@ zS#PP50Yi9DGAbPeEjlD%SingErvyAJASECUuq2I54v;N;l!|AuB#jS4NRwfER5S&1 zNe1f?thNcrtU%9JeAe-E)LP65uc1evi@qR0=2w!fN~dTJ@b8m@sV>1bOVrCdjDe4U zGx9`3Ps=Une;sY>Xx|f?sosrL?^XZW#kGq+rH0*-8Hj z_fn1fGY~WJR8CTnK6I%N>iOrX^pWF#^tH)n@m~`-E%(#zDkGzBskxl(<-y2K@Ly8d z8Fk#V%LRu~S#ZR@!YpfOXHvLy?U{v;G(__-E<{;7z`ZEO_;*Rg()|X6_1lQtMjgMR SzHR9o#{HjO_!Egb%=0gxJXzuZ diff --git a/Nodes/Flyff/flyff_character_status_node.py b/Nodes/Flyff/flyff_character_status_node.py index afd9701..3a2b5ef 100644 --- a/Nodes/Flyff/flyff_character_status_node.py +++ b/Nodes/Flyff/flyff_character_status_node.py @@ -1,46 +1,44 @@ #!/usr/bin/env python3 """ -Flyff Character Status Node (New Version): - - Has no inputs/outputs. - - Creates an OCR region in data_collector. - - Periodically grabs raw text from that region, parses it here in the node, - and sets data_manager's HP, MP, FP, EXP accordingly. - - Also updates its own text fields with the parsed values. +Flyff Character Status Node: +- Creates an OCR region in data_collector. +- Periodically grabs raw text from that region and updates status. """ import re from OdenGraphQt import BaseNode from PyQt5.QtWidgets import QMessageBox +from PyQt5.QtCore import QTimer # Corrected import from Modules import data_manager, data_collector class FlyffCharacterStatusNode(BaseNode): - __identifier__ = 'bunny-lab.io.flyff_character_status_node' - NODE_NAME = 'Flyff - Character Status' + __identifier__ = "bunny-lab.io.flyff_character_status_node" + NODE_NAME = "Flyff - Character Status" def __init__(self): super(FlyffCharacterStatusNode, self).__init__() - # Prevent duplicates + if data_manager.character_status_collector_exists: QMessageBox.critical(None, "Error", "Only one Flyff Character Status Collector node is allowed.") raise Exception("Duplicate Character Status Node.") data_manager.character_status_collector_exists = True - # Add text fields for display - self.add_text_input('hp', 'HP', text="HP: 0/0") - self.add_text_input('mp', 'MP', text="MP: 0/0") - self.add_text_input('fp', 'FP', text="FP: 0/0") - self.add_text_input('exp', 'EXP', text="EXP: 0%") + self.add_text_input("hp", "HP", text="HP: 0/0") + self.add_text_input("mp", "MP", text="MP: 0/0") + self.add_text_input("fp", "FP", text="FP: 0/0") + self.add_text_input("exp", "EXP", text="EXP: 0%") - # Create a unique region id for this node (or just "character_status") self.region_id = "character_status" data_collector.create_ocr_region(self.region_id, x=250, y=50, w=180, h=130) - # Start the data_collector background thread (if not already started) data_collector.start_collector() - - # Set the node name self.set_name("Flyff - Character Status") + # Set up a timer to periodically update character stats + self.timer = QTimer() + self.timer.timeout.connect(self.process_input) + self.timer.start(1000) # Update every second + def parse_character_stats(self, raw_text): """ Extract HP, MP, FP, EXP from the raw OCR text lines. @@ -52,6 +50,8 @@ class FlyffCharacterStatusNode(BaseNode): exp_value = 0.0 if len(lines) >= 4: + print("Processing OCR Lines:", lines) # Debugging output + # line 1: HP hp_match = re.search(r"(\d+)\s*/\s*(\d+)", lines[0]) if hp_match: @@ -82,15 +82,14 @@ class FlyffCharacterStatusNode(BaseNode): def process_input(self): """ - Called periodically by the global timer in your main application (borealis.py). + Called periodically to update character status from OCR. """ - # Grab raw text from data_collector raw_text = data_collector.get_raw_text(self.region_id) + print("Raw OCR Text:", raw_text) # Debugging OCR text reading - # Parse it hp_c, hp_t, mp_c, mp_t, fp_c, fp_t, exp_v = self.parse_character_stats(raw_text) - # Update data_manager + # Update the data manager with the parsed values data_manager.set_data_bulk({ "hp_current": hp_c, "hp_total": hp_t, @@ -101,8 +100,8 @@ class FlyffCharacterStatusNode(BaseNode): "exp": exp_v }) - # Update the node's text fields - self.set_property('hp', f"HP: {hp_c}/{hp_t}") - self.set_property('mp', f"MP: {mp_c}/{mp_t}") - self.set_property('fp', f"FP: {fp_c}/{fp_t}") - self.set_property('exp', f"EXP: {exp_v}%") + # Update the node's UI text fields + self.set_property("hp", f"HP: {hp_c}/{hp_t}") + self.set_property("mp", f"MP: {mp_c}/{mp_t}") + self.set_property("fp", f"FP: {fp_c}/{fp_t}") + self.set_property("exp", f"EXP: {exp_v}%")