From 75f68b1f599afa5a4e49c4e3cc15de0cd7ae0ec5 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Wed, 12 Feb 2025 00:53:42 -0700 Subject: [PATCH] Milestone Fixed Character Node --- .../character_status_node.cpython-312.pyc | Bin 6596 -> 8369 bytes Nodes/__pycache__/data_node.cpython-312.pyc | Bin 4868 -> 6370 bytes Nodes/character_status_node.py | 112 ++++++++++++++---- Nodes/data_node.py | 85 ++++++++++--- data_collector.py | 31 +++-- 5 files changed, 176 insertions(+), 52 deletions(-) diff --git a/Nodes/__pycache__/character_status_node.cpython-312.pyc b/Nodes/__pycache__/character_status_node.cpython-312.pyc index 65cfd02730a4d269521027c2e4b262200b9719f9..4763ff50801460500847454cedd776068aaf3273 100644 GIT binary patch delta 4012 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 beGbgul0}4lz2Q7T^bp2{s_%fT$?N?GKI9p6 delta 2365 zcmb7GU2GIp6uvV%v%B4${b_gFoo@ezLYEHxq0)k-KS1klTfi<=N(3V*YiEE$+g# zvfn*>&Uel|b92wR_uZCjwa(8R4jaJZgSIQjUdxU<1!_-xWA%Eg%$}oOw9ud($mVlE zwkY&v6X5~yqIHCkvcj*&C-$v78X1COWJrq+X<{@2HMPMeidAAqSEbmAe{EgwRZ-uu z)F{fed`o%JZg5VdfSU+N1gZ$1 z!Eofr5fN5mLC-m+Xgx9v5BI2ASnDAR!y0UG6KWBLUnamE4p{kwr6BDsoaBCHJgo0d zIpBl@A z^@Oo)jn+^Yeoq_PTjPXLk1jZys4eJoX9-nx@ z=ekREN$XQ7Yi+uybgpcE=%WM4yuFjmls#?bF1XISW|>q)^TNr+ianFAly&d_(W1@n zJU4N0oOdMh8dKJ$mD~dKf_r;mHBO-S!5(1|fQLK%8;!2GyO?PTy3z0MomF3G4ugOp zMxS_ayjC@2mRZ?+nd|``=tg$&7TMB-#s74(KJJ$FL~gdn-Rw`~#>tMgN!ny5af^Wy z+2}S`%wWNT+&&-b%3+Zw$Ac1P`?yE*l8+)ocE{*df68#n=+~m0z2?+;Na(fJjQm&5 zdY}oboDBeO95b!;dUn->mtPZlO;R~A)1#yg0ogNVI%^v7WaeRN7@RR30>hNVkA8xh z_|wme!81{%S5w8XxM%;qkO&nu8W~g-akx)AE}m3IjMgAN7*%5F@n~tKSUO>Jedtl*hmC93sB1|umdwQBvBZX5 zH`b-R&rWuvUEXEa`lM_9C3VrYDUHoz@i8<$&D=8szIaxCJCO27E9Dh)fvX+wbj)`z zY+bBuSuAgz4$SP0@4dt%`Le%T$N$~@7pqh`*M0TiI|t{Lg@cP#ZHrR-gV1b}_osPb zrZe7|C=I0eAo+vWe4`T!%e)_(m-zB;`22*gaiKfKw>~7dk1z2ZU%UAkN8FJperCxX zxWD2O?gAQbK$>%3=se$<@a;@-yH>dDL{9Ul9%n@`6cYAG;P%YdmU z14d}cgzgsX-mh~|=~enpE5>mONfzVC>1>~-45+#}bJyyw-pJsfag>eQPlcqLKytOh zmoP|X4N|)6xm(?fB>KDrzrb8><;@PuvXP3M?(t>|k zs7(sB^TCC>C1KYO7O=4yL*ZsfZUxj^(rRxeIRywDK=UQJ-R&d;a$W4og#xyf`nkczwopc7!$G1$a69HpS;!|AwvdMaBo7D3 z5(p6>uLOe&N%V}X@uaXe4MgOjeq{$dfej=bwH(DnLHLm=qJ(=po~Bx;+47$NnGLr; E0sOZ>ZU6uP diff --git a/Nodes/__pycache__/data_node.cpython-312.pyc b/Nodes/__pycache__/data_node.cpython-312.pyc index 61bf528b83237ce831a479654840154cbedbce1e..9122656633de0d2c0c7ecac2691dce629fa56629 100644 GIT binary patch literal 6370 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 diff --git a/Nodes/character_status_node.py b/Nodes/character_status_node.py index f48e256..71f6b99 100644 --- a/Nodes/character_status_node.py +++ b/Nodes/character_status_node.py @@ -2,8 +2,8 @@ """ Character Status Node -This node represents the character's status. It has no input ports and four output ports: - - HP, MP, FP, EXP. +This node represents the character's status. It has seven output ports: + - HP: Current, HP: Total, MP: Current, MP: Total, FP: Current, FP: Total, EXP. It polls an API endpoint (http://127.0.0.1:5000/data) every 500 ms to update its values. If the API call is successful, the node's title is set to "Character Status (API Connected)". If the API is down or returns an error, the title is set to "Character Status (API Disconnected)". @@ -12,6 +12,7 @@ If the API is down or returns an error, the title is set to "Character Status (A from NodeGraphQt import BaseNode from Qt import QtCore, QtGui import requests +import traceback def get_draw_stat_port(color, border_color=None, alpha=127): """ @@ -24,19 +25,16 @@ def get_draw_stat_port(color, border_color=None, alpha=127): def painter_func(painter, rect, info): painter.save() - # Draw the port circle. pen = QtGui.QPen(QtGui.QColor(*border_color)) pen.setWidth(1.8) painter.setPen(pen) semi_transparent_color = QtGui.QColor(color[0], color[1], color[2], alpha) painter.setBrush(semi_transparent_color) painter.drawEllipse(rect) - # Draw the label and current value. port = info.get('port') if port is not None: node = port.node() stat = port.name() - # Use the node's 'values' dictionary if available. value = node.values.get(stat, "N/A") if hasattr(node, 'values') else "N/A" text_rect = rect.adjusted(rect.width() + 4, 0, rect.width() + 70, 0) painter.setPen(QtGui.QColor(0, 0, 0)) @@ -51,16 +49,27 @@ class CharacterStatusNode(BaseNode): def __init__(self): super(CharacterStatusNode, self).__init__() - # Initialize the output values as a dictionary. - self.values = {"HP": "N/A", "MP": "N/A", "FP": "N/A", "EXP": "N/A"} - # Add output ports for each stat with custom painters. - self.add_output("HP", painter_func=get_draw_stat_port((255, 0, 0))) # Red for HP - self.add_output("MP", painter_func=get_draw_stat_port((0, 0, 255))) # Blue for MP - self.add_output("FP", painter_func=get_draw_stat_port((0, 255, 0))) # Green for FP - self.add_output("EXP", painter_func=get_draw_stat_port((127, 255, 212))) # Aquamarine for EXP - # Set an initial title. + + # Define exact expected keys to avoid transformation mismatches + self.values = { + "HP: Current": "N/A", "HP: Total": "N/A", + "MP: Current": "N/A", "MP: Total": "N/A", + "FP: Current": "N/A", "FP: Total": "N/A", + "EXP": "N/A" + } + + # Add output ports + self.add_output("HP: Current", painter_func=get_draw_stat_port((255, 0, 0))) + self.add_output("HP: Total", painter_func=get_draw_stat_port((255, 0, 0))) + self.add_output("MP: Current", painter_func=get_draw_stat_port((0, 0, 255))) + self.add_output("MP: Total", painter_func=get_draw_stat_port((0, 0, 255))) + self.add_output("FP: Current", painter_func=get_draw_stat_port((0, 255, 0))) + self.add_output("FP: Total", painter_func=get_draw_stat_port((0, 255, 0))) + self.add_output("EXP", painter_func=get_draw_stat_port((127, 255, 212))) + self.set_name("Character Status (API Disconnected)") - # Create a QTimer that polls the API every 500ms. + + # Start polling timer self.timer = QtCore.QTimer() self.timer.timeout.connect(self.poll_api) self.timer.start(500) @@ -68,22 +77,77 @@ class CharacterStatusNode(BaseNode): def poll_api(self): """ Polls the API endpoint to retrieve the latest character stats and updates - the node's internal values. Expects a JSON response with keys: - - "hp", "mp", "fp", "exp" + the node's internal values. """ try: response = requests.get("http://127.0.0.1:5000/data", timeout=1) + if response.status_code == 200: data = response.json() - # Update the values dictionary. - self.values["HP"] = data.get("hp", "0/0") - self.values["MP"] = data.get("mp", "0/0") - self.values["FP"] = data.get("fp", "0/0") - self.values["EXP"] = data.get("exp", "0.0000") - self.set_name("Character Status (API Connected)") - self.update() + + if isinstance(data, dict): + try: + for key, value in data.items(): + # Ensure the keys match the expected ones exactly + formatted_key = { + "hp_current": "HP: Current", + "hp_total": "HP: Total", + "mp_current": "MP: Current", + "mp_total": "MP: Total", + "fp_current": "FP: Current", + "fp_total": "FP: Total", + "exp": "EXP" + }.get(key, key) # Use mapping or fallback to raw key + + if formatted_key in self.values: + self.values[formatted_key] = str(value) + else: + print(f"[WARNING] Unexpected API key: {key} (not mapped)") + + self.set_name("Character Status (API Connected)") + self.update() + self.transmit_data() + + except Exception as e: + print("[ERROR] Error processing API response data:", e) + 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)") + else: + print(f"[ERROR] API request failed with status code {response.status_code}") self.set_name("Character Status (API Disconnected)") + except Exception as e: self.set_name("Character Status (API Disconnected)") - print("Error polling API in CharacterStatusNode:", e) + print("[ERROR] Error polling API in CharacterStatusNode:", str(e)) + print("[ERROR] Stack Trace:\n", traceback.format_exc()) + + def transmit_data(self): + """ + Sends the updated character stats to connected nodes. + """ + for stat, value in self.values.items(): + try: + port = self.get_output(stat) + + if port is None: + print(f"[ERROR] Port '{stat}' not found in node outputs. Skipping...") + continue + + if port.connected_ports(): + for connected_port in port.connected_ports(): + connected_node = connected_port.node() + if hasattr(connected_node, 'receive_data'): + try: + connected_node.receive_data(value, stat) + except Exception as e: + print(f"[ERROR] Error transmitting data to {connected_node}: {e}") + print("[ERROR] Stack Trace:\n", traceback.format_exc()) + else: + print(f"[WARNING] Connected node {connected_node} does not have receive_data method.") + + except Exception as e: + print(f"[ERROR] Error while handling port {stat}: {e}") + print("[ERROR] Stack Trace:\n", traceback.format_exc()) diff --git a/Nodes/data_node.py b/Nodes/data_node.py index f33bdeb..41ce574 100644 --- a/Nodes/data_node.py +++ b/Nodes/data_node.py @@ -1,3 +1,20 @@ +#!/usr/bin/env python3 + +""" +Data Node: + - Input: Accepts a value (string, integer, or float) from an input port. + - Output: Outputs the current value, either from the input port or set manually via a text box. + +Behavior: +- If both input and output are connected: + - Acts as a passthrough, displaying the input value and transmitting it to the output. + - Manual input is disabled. +- If only the output is connected: + - Allows manual value entry, which is sent to the output. +- If only the input is connected: + - Displays the input value but does not transmit it further. +""" + from NodeGraphQt import BaseNode class DataNode(BaseNode): @@ -6,13 +23,20 @@ class DataNode(BaseNode): def __init__(self): super(DataNode, self).__init__() + # Add input and output ports. self.add_input('Input') self.add_output('Output') + # Add a text input widget for manual entry. self.add_text_input('value', 'Value', text='') - self.manual_input_enabled = True - self.set_name(f"Data Node: {self.get_property('value')}") + # Initialize the value from the widget property. + self.process_widget_event() + self.set_name(f"Data Node: {self.value}") def post_create(self): + """ + Called after the node's widget is fully created. + Connect the text input widget's textChanged signal to process_widget_event. + """ text_widget = self.get_widget('value') if text_widget is not None: try: @@ -21,55 +45,82 @@ class DataNode(BaseNode): print("Error connecting textChanged signal:", e) def process_widget_event(self, event=None): - if self.manual_input_enabled: - current_text = self.get_property('value') - self.set_name(f"Data Node: {current_text}") - self.transmit_data(current_text) + """ + Reads the current text from the node's property and updates the node's internal value. + """ + current_text = self.get_property('value') + self.value = current_text + self.set_name(f"Data Node: {self.value}") def property_changed(self, property_name): + """ + Called when a node property changes. If the 'value' property changes, + update the internal value. + """ if property_name == 'value': self.process_widget_event() def update_stream(self): + """ + Updates the node's behavior based on the connection states. + """ input_port = self.input(0) output_port = self.output(0) if input_port.connected_ports() and output_port.connected_ports(): - self.manual_input_enabled = False + # Both input and output are connected; act as passthrough. + self.set_property('value', '') self.get_widget('value').setEnabled(False) + input_value = input_port.connected_ports()[0].node().get_property('value') + self.set_property('value', input_value) elif output_port.connected_ports(): - self.manual_input_enabled = True + # Only output is connected; allow manual input. self.get_widget('value').setEnabled(True) - self.transmit_data(self.get_property('value')) elif input_port.connected_ports(): - self.manual_input_enabled = False + # Only input is connected; display input value. self.get_widget('value').setEnabled(False) + input_value = input_port.connected_ports()[0].node().get_property('value') + self.set_property('value', input_value) else: - self.manual_input_enabled = True + # Neither input nor output is connected; allow manual input. self.get_widget('value').setEnabled(True) def on_input_connected(self, input_port, output_port): + """ + Called when an input port is connected. + """ self.update_stream() def on_input_disconnected(self, input_port, output_port): + """ + Called when an input port is disconnected. + """ self.update_stream() def on_output_connected(self, output_port, input_port): + """ + Called when an output port is connected. + """ self.update_stream() def on_output_disconnected(self, output_port, input_port): + """ + Called when an output port is disconnected. + """ self.update_stream() def receive_data(self, data, source_port_name=None): - if not self.manual_input_enabled: - self.set_property('value', data) - self.set_name(f"Data Node: {data}") - self.transmit_data(data) + """ + Receives data from connected nodes and updates the internal value. + """ + print(f"DataNode received data from {source_port_name}: {data}") # Debugging + self.set_property('value', str(data)) # Ensure it's always stored as a string + self.set_name(f"Data Node: {data}") - def transmit_data(self, data): + # Transmit data further if there's an output connection output_port = self.output(0) if output_port and output_port.connected_ports(): for connected_port in output_port.connected_ports(): connected_node = connected_port.node() if hasattr(connected_node, 'receive_data'): - connected_node.receive_data(data) + connected_node.receive_data(data, source_port_name) diff --git a/data_collector.py b/data_collector.py index e3774a5..a191645 100644 --- a/data_collector.py +++ b/data_collector.py @@ -4,6 +4,9 @@ Collector Process: - Runs the OCR engine. - Updates OCR data every 0.5 seconds. - Exposes the latest data via an HTTP API using Flask. + +This version splits the HP, MP, and FP values into 'current' and 'total' before +sending them via the API, so the Character Status Node can ingest them directly. """ import time @@ -14,10 +17,13 @@ app = Flask(__name__) # Global variable to hold the latest stats (HP, MP, FP, EXP) latest_data = { - "hp": "0/0", - "mp": "0/0", - "fp": "0/0", - "exp": "0.0000" + "hp_current": 0, + "hp_total": 0, + "mp_current": 0, + "mp_total": 0, + "fp_current": 0, + "fp_total": 0, + "exp": 0.0000 } def ocr_collector(): @@ -30,18 +36,21 @@ def ocr_collector(): while True: # Simulate updating stats: hp_current = 50 + counter % 10 - hp_max = 100 + hp_total = 100 mp_current = 30 + counter % 5 - mp_max = 50 + mp_total = 50 fp_current = 20 # fixed, for example - fp_max = 20 + fp_total = 20 exp_val = round(10.0 + (counter * 0.1), 4) latest_data = { - "hp": f"{hp_current}/{hp_max}", - "mp": f"{mp_current}/{mp_max}", - "fp": f"{fp_current}/{fp_max}", - "exp": f"{exp_val:.4f}" + "hp_current": hp_current, + "hp_total": hp_total, + "mp_current": mp_current, + "mp_total": mp_total, + "fp_current": fp_current, + "fp_total": fp_total, + "exp": exp_val } counter += 1