diff --git a/factorygame/data/vis.py b/factorygame/data/vis.py index e1abd8a..175c88d 100755 --- a/factorygame/data/vis.py +++ b/factorygame/data/vis.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 - +import itertools import re from datetime import timedelta @@ -15,14 +15,22 @@ from sqlalchemy import create_engine from sqlalchemy.orm import Session from factorygame.data.common import chose_resource, resource_needs_update, chose_recipe -from .models import Recipe, Resource +from .models import Recipe, Resource, ResourceFlow from ..helper import prompt +WORLD_OUTPUT_PORT_NAME = "World Output" +WORLD_OUTPUT_COLOR = (0, 139, 41) + +CREATE_MACHINE_PORT_NAME = "Create Machine" + INPUT_COLOR = (249, 169, 0) OUTPUT_COLOR = (204, 44, 36) OTHER_COLOR = (0, 83, 135) +graph = None + + class NodeSlider(NodeBaseWidget): def __init__(self, name, label="", parent=None): super(NodeSlider, self).__init__(parent) @@ -58,7 +66,22 @@ class GlobalInput(BaseNode): def __init__(self): super().__init__() - self.add_output("Create Machine", color=OTHER_COLOR) + self.model.width = 240 + self.set_port_deletion_allowed(True) + self.add_special_ports() + + def add_special_ports(self): + self.add_output(WORLD_OUTPUT_PORT_NAME, color=WORLD_OUTPUT_COLOR) + self.add_output(CREATE_MACHINE_PORT_NAME, color=OTHER_COLOR) + + def reorder_outputs(self): + self.delete_output(WORLD_OUTPUT_PORT_NAME) + self.delete_output(CREATE_MACHINE_PORT_NAME) + self.add_special_ports() + + def update_x_pos(self): + min_x_pos = min(map(lambda node: node.x_pos(), filter(lambda node: node != self, graph.all_nodes()))) + self.set_x_pos(min_x_pos - self.view.width - 150) class Machine(BaseNode): @@ -82,12 +105,6 @@ class Machine(BaseNode): self.set_property("name", recipe.factory.label, push_undo=False) - def in_amount_name(resource_label: str): - return f"In {resource_label} amount" - - def out_amount_name(resource_label: str): - return f"Out {resource_label} amount" - input_resources: dict[str, float] = {} for ingredient in recipe.ingredients: resource_label = ingredient.resource.label @@ -97,10 +114,12 @@ class Machine(BaseNode): f"{input_resources[resource_label]:.2f} / min" ) else: - port = self.add_input(resource_label, color=INPUT_COLOR) + port = self.add_input(resource_label, multi_input=True, color=INPUT_COLOR) port.add_accept_port_type(resource_label, PortTypeEnum.OUT.value, "factorygame.Machine") - port.add_accept_port_type("Create Machine", PortTypeEnum.OUT.value, "factorygame.GlobalInput") - self._add_text_label( + port.add_accept_port_type(resource_label, PortTypeEnum.OUT.value, "factorygame.GlobalInput") + port.add_accept_port_type(CREATE_MACHINE_PORT_NAME, PortTypeEnum.OUT.value, "factorygame.GlobalInput") + port.add_accept_port_type(WORLD_OUTPUT_PORT_NAME, PortTypeEnum.OUT.value, "factorygame.GlobalInput") + self._add_readonly_resource_text( in_amount_name(resource_label), ingredient, input_resources, @@ -119,7 +138,7 @@ class Machine(BaseNode): else: port = self.add_output(resource_label, color=OUTPUT_COLOR) port.add_accept_port_type(resource_label, PortTypeEnum.IN.value, "factorygame.Machine") - self._add_text_label( + self._add_readonly_resource_text( out_amount_name(resource_label), result, output_resources, resource_label, border_color=OUTPUT_COLOR ) @@ -137,50 +156,92 @@ class Machine(BaseNode): self.add_custom_widget(slider) slider.value_changed.connect(lambda name, value: set_performance(max(1, min(250, value)))) - def _add_text_label(self, name, flow, resource_amounts, resource_label, border_color): + def _add_readonly_resource_text( + self, + name: str, + flow: ResourceFlow, + resource_amounts: dict[str, float], + resource_label: str, + border_color: tuple, + ): resource_amounts[resource_label] = flow.amount_per_minute() - self.add_text_input(name=name, label=name, text=f"{resource_amounts[resource_label]} / min") - widget = self.get_widget(name) - line_edit_widget: QLineEdit = widget.get_custom_widget() + text = f"{resource_amounts[resource_label]} / min" + add_resource_text(node=self, name=name, text=text, border_color=border_color, readonly=True) + + +def in_amount_name(label: str): + return f"In {label} amount" + + +def out_amount_name(label: str): + return f"Out {label} amount" + + +def add_resource_text(node: BaseNode, name: str, text: str, border_color: tuple, readonly: bool = False): + node.add_text_input(name=name, label=name, text=text) + widget = node.get_widget(name) + line_edit_widget: QLineEdit = widget.get_custom_widget() + if readonly: line_edit_widget.setReadOnly(True) - group: _NodeGroupBox = widget.widget() - group.setMaximumWidth(220) - group.adjustSize() - if border_color: - stylesheet = line_edit_widget.styleSheet() - match = Machine.STYLESHEET_BORDER_COLOR_REGEX.match(stylesheet) - stylesheet = ( - stylesheet[: match.start(1)] + f"rgb({','.join(map(str, border_color))})" + stylesheet[match.end(1) :] - ) - line_edit_widget.setStyleSheet(stylesheet) - line_edit_widget.update() + group: _NodeGroupBox = widget.widget() + group.setMaximumWidth(220) + group.adjustSize() + if border_color: + stylesheet = line_edit_widget.styleSheet() + match = Machine.STYLESHEET_BORDER_COLOR_REGEX.match(stylesheet) + stylesheet = ( + stylesheet[: match.start(1)] + f"rgb({','.join(map(str, border_color))})" + stylesheet[match.end(1) :] + ) + line_edit_widget.setStyleSheet(stylesheet) + line_edit_widget.update() def on_port_connected(input_port: Port, output_port: Port): global debug if debug: print(f"Port {output_port} connected to {input_port}") - if isinstance(output_port.node(), GlobalInput) and output_port.name() == "Create Machine": - output_port.clear_connections(push_undo=False, emit_signal=False) - with Session(engine) as session: - resource_label = input_port.name() - resource = session.scalars(Resource.by_label(resource_label)).one_or_none() - if resource_needs_update(resource, recipe_info_timeout=timedelta(days=365)): - print("Please fetch resource", resource_label, "first.") - return + if isinstance(output_port.node(), GlobalInput): + global_input_node: GlobalInput = output_port.node() + resource_label = input_port.name() + if output_port.name() == CREATE_MACHINE_PORT_NAME: + output_port.clear_connections(push_undo=False, emit_signal=False) + with Session(engine) as session: + resource = session.scalars(Resource.by_label(resource_label)).one_or_none() + if resource_needs_update(resource, recipe_info_timeout=timedelta(days=365)): + print("Please fetch resource", resource_label, "first.") + return - # FIXME: use some Qt UI prompt method - recipe = chose_recipe(session=session, resource=resource, prompt=prompt) - if recipe is None: - return + # FIXME: use some Qt UI prompt method + recipe = chose_recipe(session=session, resource=resource, prompt=prompt) + if recipe is None: + return - recipe_machine = graph.create_node("factorygame.Machine", push_undo=True) - recipe_machine.assign_recipe(recipe) - recipe_machine.update() - recipe_machine.set_x_pos(input_port.node().x_pos() - recipe_machine.view.width - 100) - recipe_machine.get_output(input_port.name()).connect_to(input_port) - if recipe_machine.x_pos() - (global_input.x_pos() - global_input.view.width) < 150: - global_input.set_x_pos(recipe_machine.x_pos() - global_input.view.width - 150) + recipe_machine: Machine = graph.create_node("factorygame.Machine", push_undo=True) + recipe_machine.assign_recipe(recipe) + recipe_machine.update() + recipe_machine.set_x_pos(input_port.node().x_pos() - recipe_machine.view.width - 100) + recipe_machine.get_output(resource_label).connect_to(input_port) + global_input_node.update_x_pos() + elif output_port.name() == WORLD_OUTPUT_PORT_NAME: + output_port.clear_connections(push_undo=False, emit_signal=False) + for idx, port in enumerate(global_input_node.output_ports()): + if port.name() == resource_label: + port.connect_to(input_port, push_undo=True, emit_signal=False) + # FIXME: recalculate + return + if isinstance(input_port.node(), Machine): + machine_node: Machine = input_port.node() + new_output_port = global_input_node.add_output(name=resource_label, color=OUTPUT_COLOR) + add_resource_text( + node=global_input_node, + name=out_amount_name(resource_label), + text=str(machine_node.get_property(in_amount_name(resource_label))), + border_color=OUTPUT_COLOR, + ) + global_input_node.reorder_outputs() + global_input_node.update_x_pos() + new_output_port.connect_to(input_port, push_undo=True, emit_signal=False) + # FIXME: recalculate @click.command @@ -223,13 +284,12 @@ def main(debug: bool, search: str): graph.register_node(GlobalInput) graph_widget = graph.widget graph_widget.show() - global global_input global_input = graph.create_node("factorygame.GlobalInput", push_undo=False) recipe_machine = graph.create_node("factorygame.Machine", push_undo=False) recipe_machine.assign_recipe(recipe) graph.auto_layout_nodes([global_input, recipe_machine]) global_input.set_y_pos(recipe_machine.y_pos()) - global_input.set_x_pos(global_input.x_pos() - global_input.model.width - 150) + global_input.update_x_pos() graph.center_on([global_input, recipe_machine]) graph.port_connected.connect(on_port_connected) app.exec_()