From 39ede691d4120a8fb7ac59170695676776b7bc93 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Thu, 13 Feb 2025 00:31:06 -0700 Subject: [PATCH] Implemented Comparison Nodes and Low Health Alerts --- .../__pycache__/backdrop_node.cpython-312.pyc | Bin 9706 -> 9417 bytes .../comparison_node.cpython-312.pyc | Bin 0 -> 4963 bytes ...lyff_character_status_node.cpython-312.pyc | Bin 7910 -> 7934 bytes ...lyff_low_health_alert_node.cpython-312.pyc | Bin 0 -> 1186 bytes Nodes/backdrop_node.py | 8 -- Nodes/comparison_node.py | 128 ++++++++++++++++++ Nodes/flyff_character_status_node.py | 12 +- Nodes/flyff_low_health_alert_node.py | 22 +++ borealis_transparent.py | 104 ++++++++++++++ debug_processed.png | Bin 2821 -> 2823 bytes debug_screenshot.png | Bin 13118 -> 13652 bytes screen_overlays.py | 80 +++++++++++ 12 files changed, 340 insertions(+), 14 deletions(-) create mode 100644 Nodes/__pycache__/comparison_node.cpython-312.pyc create mode 100644 Nodes/__pycache__/flyff_low_health_alert_node.cpython-312.pyc create mode 100644 Nodes/comparison_node.py create mode 100644 Nodes/flyff_low_health_alert_node.py create mode 100644 borealis_transparent.py create mode 100644 screen_overlays.py diff --git a/Nodes/__pycache__/backdrop_node.cpython-312.pyc b/Nodes/__pycache__/backdrop_node.cpython-312.pyc index 9f205ca43a47bcab4ac33e87c5662e6c1a2ac6c2..dd8a2fecef60815ac70f703747c4757f7cb96d81 100644 GIT binary patch delta 235 zcmaFmebSTfG%qg~0}$Mpur|F}d?KGjh#!zQogsxGg)xUAmobVlmnn*g5yWT8Va{cV zVga+6b69iPqS(M}mK^q6jwlXB1||k~h7{Hoh7`6`metH4QyCbdI4jvTIW|vZ+{DZ% zyxE17jg?V#vo{wv3!}s4aK2V%M*q!s1&f#%<2DC~m@+b^PHq#u!F?%*x|L@XmHo_g?9QILEC4}$0?5Wj$WQ4qYESv9BUeV-@4C*;HWx67@M&1OX0X*_w| zf8O40wZ}Ky^o%eP69&x7AopS~_hTQb+0TN!7S|>^$m)3`ZcKD7o65sD1mcm4gw^j8 z*5I{QenloEjz&}6khXx3=zbQuGN~qrHK`J=vCn1BjT&Jw zWYoBYk&eQOgkz^aklMJ)&ICf0NoiJ@itgy8=xg;qUGH;v-E5?+&y9ZjXwQdv+Y9P7 z=-K6PI)F=dwfVq@4f}pZd9YL&j56jiLeb_C(&!zCx z-kd+_!3%ru=qG@e_S(WP58l}Gom&92<=(MjeOgIZUd}E)o;mKs4<7)21`P;)A1wD5 G7ybZ$ilHq4 diff --git a/Nodes/__pycache__/comparison_node.cpython-312.pyc b/Nodes/__pycache__/comparison_node.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d49ae69df311427484c0a4a8f015749f92c8f967 GIT binary patch literal 4963 zcmb^#OH3TewR*liKfq>SfU(y-m|ZjGW4s&xz(xxJ949uccb)x=yy-Mk4U9c>PpW&2 zA%nuf(vB5J!LlL~$x)o-5G*^tOOj)hLlP+$2Jp_f<%6Z1_J)mDu{h;b_4Ir&b`HCZ zRP*Y+SFc{ZdcWe|fq)mm7r*nn$zSY7=s$E~ea<4Vc@Tix2%{Ln9JVF7XBgs4%v3!Zt}+Lv(BhX`aXw{_nYc;N+JnORfJI$%e`Jf=n~E-skE;33KwQnAwgjw zr4n5e+FJ)&1xdj|>tJiNo3W0ib)feOV~Q-O3}(E*`o65ClX{6SB@;B(qkQA8D+N-PomR780}nQ-z{@mxa*EtQmJH9?<}1<(=>DPw`KGl5M6 z31A6#sY-HI(B-STFs@z|lF~RF0L)W7d$;!sYVjy%@Pm>jQv=7%D=06v4; zlTnO=8?<3AX2-UeBa0?DxII4R#2)Op#>HINc@4$f*aahx-7tE9!UM&dMOV3)?*kO| z8a3rxrF+ip8BZ(9Y!|54olv{!Y0N7Z6`*d_mbsY^#KA90p9>)BHk)?<`!nt~(n}=W zQXgf|Pkad7FH$&|Uvih&N&x2W7bU-v!&b%NDJ?Al*j~i}yKeum13M=XcKw=r;IAPP z59}4%fj>~@2ae{KIjkKyG>f7&qXsu%aD#@+^ge@ESj8p;hRy{- z9f?p+iv|p*mIh@BO=|`(iU}p5i=yF?Fa{W+(C|{wOf^Ogg~6Z4RgvZpb53Piypysn z&LlAW7`rGOPiwk5ZJ~UctiKOoJO)v0IIl>_v~2Kz6BTJ%HUcT4#v!SgvZ4;d0qJSN z2$%=3WO$MZMHXe8&<&3$#*-2(5H$y=4;gG>t8=9H;(J8B1ZhniR3T?16WT=@l-k8@ zaoC-jB~7qwE>w<3%Ghlg0Wv?*{tUy51@xE?+)$Qqj_+LOyEk?tPvECD*J~EFbv|6+ z{Z(-7_1YzVosVp|fGf|3b9{K|+soc($ld2%;lt07+vD44EYr>{AOGuqAguHKg{D2f zto?cIa$v3LXg2W3*Yu?6&3x0bT+^|&rW4sSPy7vee(vk97`cqwPVq?B8J4;kh*je^R|L)qagXAK%xWB_7|=A&(& z6$#|<>*<)IVRXqVniWzk2|H%4QeK+3D>iJOx4%MHhZV8&j*J6zb^IS)UrYbWx;mMz zP7l&cnKV<%pYyH?th1!I8&0-U?k;oi*fsCTK=HZUPNO#C$}ni&qZ|b+Ux7V_5MVn1 z>#4vFF{}VsZv__o0(ct$>#M+CWLPg?{S515yR}ek#zSn_p>UXkOj^Pai^`o`kdbGQQV&EL_d`4XRpw3#$p&R`UG`oxb?MHg466t>H%oDaYM0l`+ecqLE&bvQB zi`xx zCM+=l-Ib!iG8uOYlSCS49oGStj1LGCM4hgXB`{(-rRk7Krv=)+^a(1oXk;d#$pV%q zBjDjxD>7lUiKHs&!pxMcK(DH4iSeY|DM+alZRMcbGn-Io^!kQvGFen^YrFd@)IcrMJQOppj{V_GRo3+!+whO1bY-BXe# z={g}DRGk3XPx+nl63~7a7ZqK%|T2k{Pjv^)+rFTHu|Y<9Q=Z1_>IDPP}~t8cp-TdVKM@`YWES=SR^{bFX-x4+QP zvLxU9-u00}pkbvsx*CX9V>(s?9TfB3o1ZeCrG2Y`_DaM`^O4oSk?KWnuLj;OVzMJo zT__y6dH$|M>wdAlXwLD?OI;7@moNMwb}x2+-#UK+D53D}%Qr9IjoebQzDK^srM`Ul zXfAwoJ$$^-7|Ayt&NUwXyz!kyTOqRlx1nE!e$%{I^SHicseifuVSR5Q*qRR>$^{So z(eW4WpS=J2cqMq~VerGJZWQ{)h8^uWk?ni&ybtXP-#N2V`_?mL^RyI#&DmiXemuP4 zgz1(>!{zVYjU%VfKTg%34%+`&&%qe4>?COBr}ND=7+%$lSX~0`xbJbJ_c-QT_w1&J zdrjA-K}X&NDH@dre#pGg*sUsND+wKIG2_iewmId?ENKw2Ni)C#s_do+6kqh!^e0e> z5gDe#2n^NAHk35M39YPe+pF}gGlV%d zq5qg5-=V`O9cVf?0>x)eTuvrM5$@zIz-sjV-Zj%L?XKAaj=HTzZ~DbGRA_i-VYE;e zSvd3D(dOK{d6w&Qe#AYS<2dKuXD&9_oRR Lc1Eqqrg9qrx}v2>}qZI|--*0V9*(6Be@q4W9u7JJbj~ zv!xC)1+(81zXAkE)EG&Vq82O!N7WQalhPI<1VhyjM3eXyN(4XD3_!C+7X}9bB9l!X EvE8d4lmGw# diff --git a/Nodes/__pycache__/flyff_low_health_alert_node.cpython-312.pyc b/Nodes/__pycache__/flyff_low_health_alert_node.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35e9fd0033907470340f5032b3ab72b1a6af7270 GIT binary patch literal 1186 zcmaJ=&1=*^6o0cxw%K;KRIzIDgXlqw+C{~K)DN^;wNkf=MIjW1G&9>qXOnf3rQO9t z4+SeIdJ)A_uTq5`{SyQ)3Kb>Tlc(MaRV> zXFOG>EtwWrKY-u?a^&C;22O?%M`IaBpNBGG+MMYa^Dsl4>`k!sB%jur2xrtrje`@l zAgK2XccSQr#j>cE%T$EZbc(woo}#YcGNysb+<8d_t$u4HoVKo{Ve7mKd!$VWZ5BV( z`T7E?XXH7=XqytZ`NDm8+y$`M@)KO&>1b#Dv5c?`)0n=fH+}!iGcYgvS6=Q^bYv!k)pd%TgqbP>x?xT zSof`?3Y5#Xt*ySpT~-+?sp{h#4KEa-w2j1wYE>?!YA`V=^#ebqG|9V+QBO@3Cz0gT z$`7iw*v=$+#6>yDQR)ddiYSd#MQirg$x8#{cwO#rFQ$W`#0mJ(xEf+K{-3!Ot999f z1sc7Z=U!|RDPSnNhH7ILzM6$c;>E>}=AmWt$XXV1h0kW+N3-w6frb4Gm8G#|bKr}a z-{cFH*s^&l&3w}Ru=~S~<8S-tyFZy%*0CVi9jf2Jt#ItOlhR7aYC_>WrT6eyTaj+0 z!gvt-WuHro=1QX%hiK{iwV{-EQfM87$AzHOCUTpitnS=7*^4?o*;J_)8|_84F$YL$ z`#CaOLc8Vg?C?+BG=|9), Less Than (<), + Greater Than or Equal (>=), Less Than or Equal (<=). + - Displays the computed result in a read-only text box labeled "Result". +""" + +from OdenGraphQt import BaseNode + +class ComparisonNode(BaseNode): + __identifier__ = 'bunny-lab.io.comparison_node' + NODE_NAME = 'Comparison Node' + + def __init__(self): + super(ComparisonNode, self).__init__() + + # ---------------------------------------------------------------------- + # Initialization Section: + # - Create two input ports: A, B + # - Create one output port: Result + # - Add a combo box for logical operator selection + # - Add a text input for displaying the computed result + # ---------------------------------------------------------------------- + self.add_input('A') + self.add_input('B') + self.add_output('Result') + + # Operator combo box (==, !=, >, <, >=, <=) + self.add_combo_menu('operator', 'Operator', items=[ + 'Equal (==)', + 'Not Equal (!=)', + 'Greater Than (>)', + 'Less Than (<)', + 'Greater Than or Equal (>=)', + 'Less Than or Equal (<=)' + ]) + + # Text input for displaying the computed result. + # We'll make it read-only by accessing the underlying QLineEdit. + self.add_text_input('calc_result', 'Result', text='0') + result_widget = self.get_widget('calc_result') # This is a NodeLineEdit wrapper + if result_widget: + # Get the underlying QLineEdit + line_edit = result_widget.get_custom_widget() + # Make the QLineEdit read-only + line_edit.setReadOnly(True) + + self.value = 0 + self.set_name("Comparison Node") + self.process_input() + + def process_input(self, event=None): + """ + Compute Section: + - For each input port (A, B), if connected, grab the 'value' from + the upstream node; otherwise default to 0.0. + - Convert to float when possible, apply the selected comparison operator, + update the "Result" text box, node title, and output port. + """ + # Gather input A + input_a = self.input(0) + if input_a and input_a.connected_ports(): + a_raw = input_a.connected_ports()[0].node().get_property('value') + else: + a_raw = 0.0 + + # Gather input B + input_b = self.input(1) + if input_b and input_b.connected_ports(): + b_raw = input_b.connected_ports()[0].node().get_property('value') + else: + b_raw = 0.0 + + # Convert raw inputs to float if possible, otherwise keep as-is for string comparison. + try: + a_val = float(a_raw) + b_val = float(b_raw) + except (ValueError, TypeError): + a_val = a_raw + b_val = b_raw + + # Retrieve the selected operator from the combo box. + operator = self.get_property('operator') + result = False + + if operator == 'Equal (==)': + result = a_val == b_val + elif operator == 'Not Equal (!=)': + result = a_val != b_val + elif operator == 'Greater Than (>)': + result = a_val > b_val + elif operator == 'Less Than (<)': + result = a_val < b_val + elif operator == 'Greater Than or Equal (>=)': + result = a_val >= b_val + elif operator == 'Less Than or Equal (<=)': + result = a_val <= b_val + + # Convert boolean result to integer (1 for True, 0 for False) + self.value = 1 if result else 0 + + # Update the read-only text input and node title. + self.set_property('calc_result', str(self.value)) + + # Transmit the numeric result to any connected output nodes. + output_port = self.output(0) + if output_port and output_port.connected_ports(): + for cp in output_port.connected_ports(): + connected_node = cp.node() + if hasattr(connected_node, 'receive_data'): + connected_node.receive_data(self.value, source_port_name='Result') + + def on_input_connected(self, input_port, output_port): + self.process_input() + + def on_input_disconnected(self, input_port, output_port): + self.process_input() + + def property_changed(self, property_name): + if property_name in ['operator']: + self.process_input() + + def receive_data(self, data, source_port_name=None): + self.process_input() diff --git a/Nodes/flyff_character_status_node.py b/Nodes/flyff_character_status_node.py index b9d57f4..9275104 100644 --- a/Nodes/flyff_character_status_node.py +++ b/Nodes/flyff_character_status_node.py @@ -36,7 +36,7 @@ def get_draw_stat_port(color, border_color=None, alpha=127): class CharacterStatusNode(BaseNode): __identifier__ = 'bunny-lab.io.flyff_character_status_node' - NODE_NAME = 'Character Status' + NODE_NAME = 'Flyff - Character Status' def __init__(self): super(CharacterStatusNode, self).__init__() @@ -58,7 +58,7 @@ class CharacterStatusNode(BaseNode): self.add_output("FP: Total", painter_func=get_draw_stat_port((36, 197, 28))) self.add_output("EXP", painter_func=get_draw_stat_port((52, 195, 250))) - self.set_name("Character Status (API Disconnected)") + self.set_name("Flyff - Character Status (API Disconnected)") # Start polling timer self.timer = QtCore.QTimer() @@ -95,7 +95,7 @@ class CharacterStatusNode(BaseNode): else: print(f"[WARNING] Unexpected API key: {key} (not mapped)") - self.set_name("Character Status (API Connected)") + self.set_name("Flyff - Character Status (API Connected)") self.update() self.transmit_data() @@ -104,14 +104,14 @@ class CharacterStatusNode(BaseNode): print("[ERROR] Stack Trace:\n", traceback.format_exc()) else: print("[ERROR] Unexpected API response format (not a dict):", data) - self.set_name("Character Status (API Disconnected)") + self.set_name("Flyff - Character Status (API Disconnected)") else: print(f"[ERROR] API request failed with status code {response.status_code}") - self.set_name("Character Status (API Disconnected)") + self.set_name("Flyff - Character Status (API Disconnected)") except Exception as e: - self.set_name("Character Status (API Disconnected)") + self.set_name("Flyff - Character Status (API Disconnected)") print("[ERROR] Error polling API in CharacterStatusNode:", str(e)) print("[ERROR] Stack Trace:\n", traceback.format_exc()) diff --git a/Nodes/flyff_low_health_alert_node.py b/Nodes/flyff_low_health_alert_node.py new file mode 100644 index 0000000..708c69e --- /dev/null +++ b/Nodes/flyff_low_health_alert_node.py @@ -0,0 +1,22 @@ +from OdenGraphQt import BaseNode +from OdenGraphQt.constants import NodePropWidgetEnum +from OdenGraphQt.widgets.node_widgets import NodeLineEditValidatorCheckBox + +class CheckboxNode(BaseNode): + + # set a unique node identifier. + __identifier__ = 'bunny-lab.io.flyff_low_health_alert_node' + + # set the initial default node name. + NODE_NAME = 'Flyff - Low Health Alert' + + def __init__(self): + super(CheckboxNode, self).__init__() + + # Create checkboxes to decide which kind of alert(s) to utilize. + self.add_checkbox('cb_1', '', 'Sound Alert', True) + self.add_checkbox('cb_2', '', 'Visual Alert', True) + + # Create Input Port + self.add_input('Toggle (1 = On | 0 = Off)', color=(200, 100, 0)) + #self.add_output('out', color=(0, 100, 200)) \ No newline at end of file diff --git a/borealis_transparent.py b/borealis_transparent.py new file mode 100644 index 0000000..053a6fa --- /dev/null +++ b/borealis_transparent.py @@ -0,0 +1,104 @@ +import os +import sys +import pkgutil +import importlib +import inspect +from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QMenu, QUndoStack +from PyQt5.QtCore import Qt, QTimer, QRect +from PyQt5.QtGui import QColor, QPainter, QPen, QBrush +from OdenGraphQt import NodeGraph, BaseNode + +# Force qtpy to use PyQt5 explicitly +os.environ["QT_API"] = "pyqt5" + +# --- PATCH OdenGraphQt to use QtWidgets.QUndoStack instead of QtGui.QUndoStack --- +import qtpy.QtGui +import qtpy.QtWidgets + +if not hasattr(qtpy.QtGui, "QUndoStack"): # Ensure patching only if necessary + qtpy.QtGui.QUndoStack = qtpy.QtWidgets.QUndoStack + +# --- END PATCH --- + +class TransparentGraphWindow(QMainWindow): + def __init__(self): + super().__init__() + + # Enable transparency & always-on-top behavior + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool) + self.setAttribute(Qt.WA_TranslucentBackground, True) + + # Get full-screen size for overlay + screen_geometry = QApplication.primaryScreen().geometry() + self.setGeometry(screen_geometry) + + # Create Node Graph + self.graph = NodeGraph() + self.graph.widget.setParent(self) + self.graph.widget.setGeometry(self.rect()) + + # Make bottom-left corner interactive for context menu + self.context_menu_area = QRect(10, self.height() - 40, 50, 30) + + # Load nodes dynamically + self.import_nodes() + + # Global update timer for processing nodes + self.timer = QTimer() + self.timer.timeout.connect(self.update_nodes) + self.timer.start(500) + + def import_nodes(self): + """Dynamically import all custom node classes from the 'Nodes' package.""" + try: + package_name = 'Nodes' + package = importlib.import_module(package_name) + for loader, module_name, is_pkg in pkgutil.walk_packages(package.__path__, package.__name__ + "."): + module = importlib.import_module(module_name) + for name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, BaseNode) and obj.__module__ == module.__name__: + self.graph.register_node(obj) + except Exception as e: + print(f"Error loading nodes: {e}") + + def update_nodes(self): + """Calls process_input() on all nodes, if applicable.""" + for node in self.graph.all_nodes(): + if hasattr(node, "process_input"): + try: + node.process_input() + except Exception as e: + print(f"Error processing node {node}: {e}") + + def mousePressEvent(self, event): + """Override mouse press to handle context menu in bottom-left area.""" + if event.button() == Qt.RightButton and self.context_menu_area.contains(event.pos()): + self.show_context_menu(event.globalPos()) + + def show_context_menu(self, position): + """Displays a right-click context menu.""" + menu = QMenu(self) + quit_action = QAction("Quit", self) + quit_action.triggered.connect(self.close) + menu.addAction(quit_action) + menu.exec_(position) + + def paintEvent(self, event): + """Render transparent overlay and the small clickable menu area.""" + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + + # Draw semi-transparent context menu area + painter.setBrush(QBrush(QColor(50, 50, 50, 150))) # Dark semi-transparent box + painter.setPen(QPen(QColor(200, 200, 200, 200))) + painter.drawRect(self.context_menu_area) + + painter.setFont(self.font()) + painter.setPen(QColor(255, 255, 255)) + painter.drawText(self.context_menu_area, Qt.AlignCenter, "Menu") + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = TransparentGraphWindow() + window.show() + sys.exit(app.exec_()) diff --git a/debug_processed.png b/debug_processed.png index 4d17f78253d4074bf5116129b304c02165269104..85eebd722147fd0d524016f46dd764e44308233b 100644 GIT binary patch delta 2671 zcmV-#3Xt`M7Kav)BrDEIL_t(|obB9OyXq(mfMM9r`u~5KxzK700YXb@;(YJLY?Xsq zkkX?Q0+Rs)6n}te@#z3|((4~Fq;zH*-jB!qoOPI*e5W;&n;`WbF){j+eTXfUuC$%bA3383UrvrFP;XNKP zq!5xYG!sTE_9mwDO_qXO!N_jqGwknw%T-k|TDqn9{C}{!2y!MH$x$9|=3{7J*j)rU zm5t;GL9-ja?0P>gf}F-oa>V27@oi_)HredPN|1|~I3O)f5bhZrol=(*Ahz+IO99I4Q_gJS2z4ybbR0sd+qr4-3iByzKO?(<_esQN1wk6M_s$6{H;| zjyX21Yk!Q5dm5jD*qR;9?6&$p2686IG%d(QEUX-x_o%gRJ!o!BG&;Xr%RrFJSXwz^ zim&mJE=<3+s7mR=4bDZ7lNeb!zH4vc08Lu3T?9FgmE?%kH|?rn z@_}BnCo4g&VkJ4w(_WoY?N#oYz==UFWF$Gn6TgPEf3@r^wh@NpksxO>k{p?_CHo;R zg1p3La_FUyKM{WZu~;B( zP@325{mH4H8!N)Fz{76kFw`;REAqa$EG?X#6eD3Qr0p zv{lAt>4#GD0Db*|)D5+EgRS(oIt&9`tQ_-n=Wor_ z`B*sqZ^@zHWa3u%iR9>9xcrvfJMl)snCB;wBb$rr*!L%;eg$l47)JO{j>Aybx68W^ zCX9qJmA~XD20k{*em;y_e>pgn?TY&ivN9FW*n?1lbh7}`2UI9oZyo89Zz>a$_^+N*d=ju50t*^haW9u-qgO5-`7m0_6SD>B**X# zt$TLtM#5OhL2`r;Gg?y<_r>S0V(V_Y-5!Q1_S1VC)W(BWhjAA#P^m6pG%GC%!xS$o zM^pYA)}nOHq!L}We~pLaXddVb zXNaj@eC4}VhGBz;b8o7e`Q4&R(MH{xPLLYDqm@9GmJ$XBuD&gbK6_}&}MBzyKS@E zv96kJy<~@nV~#89@Ug9Q4zZ*wKf97W2P?-?K4sM_fVNB*dIdG$&OO8vs@_F03gCmMQf2YY|>VJzh%Irc-_e2Qkgt+JD4VOZiM zIljc-%%?+XJQmj5YReQ|%?iU5FUgS(|2BWiT>T#-wA;3eR)%4QhZw)3`5xa`7RMlu zdZ+E7VrMLtgkg${DIl|jxVlYt3mboLKRK3!VTzC4O02$leNx@M_uI}4Ls{r0H}K)+ zFcFhs1uBye0|bA7^WxJ1n3BFP%YkVYHY#TgvJ$bw)cd?1MyeH@@NZ5xzadp#_X}H< zuf);rC#96K8cA$+G8|Tpuh$U9-HW_19$zWC(tYQ#=Jo}`7bRg0ilO+Z={`PGZeMngl z^Bu{_C4l6($(7s)%@3+=4>hkH^}LWAZ{iMq(bwjp@JiPLJF7d}v-+U-eCQ;{d>4;ZV)ae4=A}w9 zlH;V_m}`GBWbll4xxc3aup*`qLfT*Es?VEhY4}#E^`p&=Bvoo&*_w2o#mXU>kQ*23 zpY1c28JXXD8NbW^jm=q?;` z?tU>HbNB2Ww>Nexvi6{k-1Eq9(~Nc`svWL~S4e+PW97*82DU%&@wmapB<@(Qo~p=g zqoR%)$+5`}G~^Ze^O<}Yk=MG*@UH4yCbC<3E1#%p9qFfw=K)LZ>U~2@$G2J}$6j_j zwDXYOc(2-mE1e2b_OBMZm7N=R2hH8kaA(R|-{;BRxjq;LE5}PQ-ZvjNROa`lTfgyK z=Oll}*V%`+jEdi@a35nk7LwyR+6-%+HrPsUQGT1C6eRqc)k4k<375s6n%dbO%)Trud=_FI2Z@XvH51l`~PbY#lr0z zu`$g1i%(@&>M+iyRq;dF;bW# d0}>aY^&eRL6HRBY&Sd}q002ovPDHLkV1iV9M%4fS delta 2660 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 Y0lA7F+_W;-S^xk507*qoM6N<$f*r=L>i_@% diff --git a/screen_overlays.py b/screen_overlays.py new file mode 100644 index 0000000..4ce2665 --- /dev/null +++ b/screen_overlays.py @@ -0,0 +1,80 @@ +import sys +from PyQt5.QtWidgets import QApplication, QWidget +from PyQt5.QtCore import Qt, QRect, QPoint +from PyQt5.QtGui import QPainter, QPen, QColor, QFont, QFontMetrics + +class OverlayCanvas(QWidget): + """ + UI overlay for drawing and interacting with on-screen elements. + """ + def __init__(self, parent=None): + super().__init__(parent) + + # **Full-screen overlay** + screen_geo = QApplication.primaryScreen().geometry() + self.setGeometry(screen_geo) # Set to full screen + + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) + self.setAttribute(Qt.WA_TranslucentBackground, True) + self.setAttribute(Qt.WA_NoSystemBackground, True) + self.setAttribute(Qt.WA_OpaquePaintEvent, False) + self.setAttribute(Qt.WA_AlwaysStackOnTop, True) + + # **Helper Object: Low Health Alert** + self.helper_LowHealthAlert = QRect(250, 300, 900, 35) # Adjusted width and height + self.dragging = False + self.resizing = False + self.drag_offset = QPoint() + + def paintEvent(self, event): + """Draw the helper overlay objects.""" + painter = QPainter(self) + painter.setPen(Qt.NoPen) + painter.setBrush(QColor(255, 0, 0)) # Solid red rectangle + painter.drawRect(self.helper_LowHealthAlert) + + # Draw bold white text centered within the rectangle + font = QFont("Arial", 14, QFont.Bold) # Scaled text + painter.setFont(font) + painter.setPen(QColor(255, 255, 255)) + + text = "LOW HEALTH" + metrics = QFontMetrics(font) + text_width = metrics.horizontalAdvance(text) + text_height = metrics.height() + text_x = self.helper_LowHealthAlert.center().x() - text_width // 2 + text_y = self.helper_LowHealthAlert.center().y() + text_height // 4 + + painter.drawText(text_x, text_y, text) + + def mousePressEvent(self, event): + """Detect clicks for dragging and resizing the helper object.""" + if event.button() == Qt.LeftButton: + if self.helper_LowHealthAlert.contains(event.pos()): + if event.pos().x() > self.helper_LowHealthAlert.right() - 10 and event.pos().y() > self.helper_LowHealthAlert.bottom() - 10: + self.resizing = True + else: + self.dragging = True + self.drag_offset = event.pos() - self.helper_LowHealthAlert.topLeft() + + def mouseMoveEvent(self, event): + """Handle dragging and resizing movements.""" + if self.dragging: + self.helper_LowHealthAlert.moveTopLeft(event.pos() - self.drag_offset) + self.update() + elif self.resizing: + new_width = max(150, event.pos().x() - self.helper_LowHealthAlert.x()) + new_height = max(20, event.pos().y() - self.helper_LowHealthAlert.y()) + self.helper_LowHealthAlert.setSize(new_width, new_height) + self.update() + + def mouseReleaseEvent(self, event): + """End dragging or resizing event.""" + self.dragging = False + self.resizing = False + +if __name__ == '__main__': + app_gui = QApplication(sys.argv) + overlay_window = OverlayCanvas() + overlay_window.show() + sys.exit(app_gui.exec_())