diff --git a/.gitignore b/.gitignore index a4d6d9c..f417792 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vs/ \ No newline at end of file +.vs/ +Borealis-Workflow-Automation-Tool/ \ No newline at end of file diff --git a/Data/Modules/data_collector.py b/Data/Modules/data_collector.py index 99eb4f0..50ef5fd 100644 --- a/Data/Modules/data_collector.py +++ b/Data/Modules/data_collector.py @@ -35,7 +35,6 @@ regions = {} app_instance = None - def _ensure_qapplication(): """ Ensures that QApplication is initialized before creating widgets. @@ -45,6 +44,15 @@ def _ensure_qapplication(): if app_instance is None: app_instance = QApplication(sys.argv) # Start in main thread +def capture_region_as_image(region_id): + collector_mutex.lock() + if region_id not in regions: + collector_mutex.unlock() + return None + x, y, w, h = regions[region_id]['bbox'][:] + collector_mutex.unlock() + screenshot = ImageGrab.grab(bbox=(x, y, x + w, y + h)) + return screenshot def create_ocr_region(region_id, x=250, y=50, w=DEFAULT_WIDTH, h=DEFAULT_HEIGHT, color=(255, 255, 0), thickness=2): """ diff --git a/Data/Modules/data_manager.py b/Data/Modules/data_manager.py index b07b4a5..53732db 100644 --- a/Data/Modules/data_manager.py +++ b/Data/Modules/data_manager.py @@ -1,7 +1,7 @@ -# Modules/data_manager.py import threading import time -from flask import Flask, jsonify +import base64 +from flask import Flask, jsonify, Response from PyQt5.QtCore import QMutex # Global datastore for character metrics @@ -21,6 +21,9 @@ data_mutex = QMutex() # Flag to ensure only one character status collector node exists character_status_collector_exists = False +# A place to store the screenshot in base64 +status_screenshot_base64 = "" + # Flask Application app = Flask(__name__) @@ -68,6 +71,47 @@ def fp_api(): "fp_total": get_data()["fp_total"] }) +@app.route('/status_screenshot') +def status_screenshot(): + """ + Returns an HTML page that displays the stored screenshot and + automatically refreshes it every second without requiring a manual page reload. + """ + html = """ + + + Live Flyff Character Status + + + + + + + """ + return html + +@app.route('/status_screenshot_data') +def status_screenshot_data(): + """ + Serves the raw PNG bytes (decoded from base64) used by in /status_screenshot. + """ + data_mutex.lock() + encoded = status_screenshot_base64 + data_mutex.unlock() + + if not encoded: + # No image captured yet, return HTTP 204 "No Content" + return "", 204 + + raw_img = base64.b64decode(encoded) + return Response(raw_img, mimetype='image/png') + def start_api_server(): """ Starts the Flask API server in a separate daemon thread. @@ -101,3 +145,12 @@ def set_data_bulk(metrics_dict): data_mutex.lock() data_store.update(metrics_dict) data_mutex.unlock() + +def set_status_screenshot(encoded_str): + """ + Called by the OCR node to store the base64-encoded screenshot data. + """ + global status_screenshot_base64 + data_mutex.lock() + status_screenshot_base64 = encoded_str + data_mutex.unlock() diff --git a/Data/Nodes/Flyff/flyff_character_status_node.py b/Data/Nodes/Flyff/flyff_character_status_node.py index 184f9dc..fc331d6 100644 --- a/Data/Nodes/Flyff/flyff_character_status_node.py +++ b/Data/Nodes/Flyff/flyff_character_status_node.py @@ -3,12 +3,19 @@ Flyff Character Status Node: - Creates an OCR region in data_collector. - Periodically grabs raw text from that region and updates status. +- ALSO: Captures a screenshot from the same region, converts to base64, + and updates data_manager so the Flask server can serve it. """ import re +import base64 +from io import BytesIO + from OdenGraphQt import BaseNode from PyQt5.QtWidgets import QMessageBox from PyQt5.QtCore import QTimer # Corrected import + +# Import the existing modules from Modules import data_manager, data_collector class FlyffCharacterStatusNode(BaseNode): @@ -29,7 +36,10 @@ class FlyffCharacterStatusNode(BaseNode): self.add_text_input("exp", "EXP", text="EXP: 0%") self.region_id = "character_status" - data_collector.create_ocr_region(self.region_id, x=250, y=50, w=180, h=130, color=(255, 255, 0), thickness=2) + data_collector.create_ocr_region( + self.region_id, x=250, y=50, w=180, h=130, + color=(255, 255, 0), thickness=2 + ) data_collector.start_collector() self.set_name("Flyff - Character Status") @@ -80,14 +90,14 @@ class FlyffCharacterStatusNode(BaseNode): def process_input(self): """ - Called periodically to update character status from OCR. + Called periodically to update character status from OCR, + and also capture the screenshot to display via Flask. """ + # 1) Update the text-based status (same as before). raw_text = data_collector.get_raw_text(self.region_id) -# print("Raw OCR Text:", raw_text) # Debugging OCR text reading - hp_c, hp_t, mp_c, mp_t, fp_c, fp_t, exp_v = self.parse_character_stats(raw_text) - # Update the data manager with the parsed values + # Update data_manager with the parsed values data_manager.set_data_bulk({ "hp_current": hp_c, "hp_total": hp_t, @@ -103,3 +113,12 @@ class FlyffCharacterStatusNode(BaseNode): 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}%") + + # 2) Capture the screenshot used by OCR and store as base64 + screenshot_img = data_collector.capture_region_as_image(self.region_id) + if screenshot_img: + # Convert PIL image to base64 + buf = BytesIO() + screenshot_img.save(buf, format='PNG') + image_b64 = base64.b64encode(buf.getvalue()).decode('utf-8') + data_manager.set_status_screenshot(image_b64)