flixopt 2.1.7__py3-none-any.whl → 2.1.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flixopt might be problematic. Click here for more details.

flixopt/network_app.py CHANGED
@@ -1,13 +1,14 @@
1
- import json
1
+ from __future__ import annotations
2
+
2
3
  import logging
3
4
  import socket
4
5
  import threading
5
- from typing import Any, Dict, List
6
+ from typing import TYPE_CHECKING, Any
6
7
 
7
8
  try:
8
9
  import dash_cytoscape as cyto
9
10
  import dash_daq as daq
10
- import networkx
11
+ import networkx as nx
11
12
  from dash import Dash, Input, Output, State, callback_context, dcc, html, no_update
12
13
  from werkzeug.serving import make_server
13
14
 
@@ -17,9 +18,13 @@ except ImportError as e:
17
18
  DASH_CYTOSCAPE_AVAILABLE = False
18
19
  VISUALIZATION_ERROR = str(e)
19
20
 
21
+ if TYPE_CHECKING:
22
+ from .flow_system import FlowSystem
23
+
24
+ import networkx as nx
25
+
20
26
  from .components import LinearConverter, Sink, Source, SourceAndSink, Storage
21
- from .elements import Bus, Component, Flow
22
- from .flow_system import FlowSystem
27
+ from .elements import Bus
23
28
 
24
29
  logger = logging.getLogger('flixopt')
25
30
 
@@ -110,8 +115,16 @@ class VisualizationConfig:
110
115
  ]
111
116
 
112
117
 
113
- def flow_graph(flow_system: FlowSystem) -> networkx.DiGraph:
118
+ def flow_graph(flow_system: FlowSystem) -> nx.DiGraph:
114
119
  """Convert FlowSystem to NetworkX graph - simplified and more robust"""
120
+ if not DASH_CYTOSCAPE_AVAILABLE:
121
+ raise ImportError(
122
+ 'Network visualization requires optional dependencies. '
123
+ 'Install with: pip install flixopt[viz] or '
124
+ 'pip install dash dash-cytoscape networkx werkzeug. '
125
+ f'Original error: {VISUALIZATION_ERROR}'
126
+ )
127
+
115
128
  nodes = list(flow_system.components.values()) + list(flow_system.buses.values())
116
129
  edges = list(flow_system.flows.values())
117
130
 
@@ -141,7 +154,7 @@ def flow_graph(flow_system: FlowSystem) -> networkx.DiGraph:
141
154
  else:
142
155
  return 'rectangle'
143
156
 
144
- graph = networkx.DiGraph()
157
+ graph = nx.DiGraph()
145
158
 
146
159
  # Add nodes with attributes
147
160
  for node in nodes:
@@ -168,7 +181,7 @@ def flow_graph(flow_system: FlowSystem) -> networkx.DiGraph:
168
181
  return graph
169
182
 
170
183
 
171
- def make_cytoscape_elements(graph: networkx.DiGraph) -> List[Dict[str, Any]]:
184
+ def make_cytoscape_elements(graph: nx.DiGraph) -> list[dict[str, Any]]:
172
185
  """Convert NetworkX graph to Cytoscape elements"""
173
186
  elements = []
174
187
 
@@ -225,7 +238,7 @@ def create_color_picker_input(label: str, input_id: str, default_color: str):
225
238
  )
226
239
 
227
240
 
228
- def create_style_section(title: str, children: List):
241
+ def create_style_section(title: str, children: list):
229
242
  """Create a collapsible section for organizing controls"""
230
243
  return html.Div(
231
244
  [
@@ -378,7 +391,7 @@ def create_sidebar():
378
391
  )
379
392
 
380
393
 
381
- def shownetwork(graph: networkx.DiGraph):
394
+ def shownetwork(graph: nx.DiGraph):
382
395
  """Main function to create and run the network visualization"""
383
396
  if not DASH_CYTOSCAPE_AVAILABLE:
384
397
  raise ImportError(f'Required packages not available: {VISUALIZATION_ERROR}')
@@ -535,10 +548,46 @@ def shownetwork(graph: networkx.DiGraph):
535
548
 
536
549
  return sidebar_style, main_style
537
550
 
551
+ # Combined callback to handle both color scheme changes and reset
552
+ @app.callback(
553
+ [
554
+ Output('bus-color-picker', 'value'),
555
+ Output('source-color-picker', 'value'),
556
+ Output('sink-color-picker', 'value'),
557
+ Output('storage-color-picker', 'value'),
558
+ Output('converter-color-picker', 'value'),
559
+ ],
560
+ [Input('color-scheme-dropdown', 'value'), Input('reset-btn', 'n_clicks')],
561
+ )
562
+ def update_color_pickers(color_scheme, reset_clicks):
563
+ """Update color pickers when color scheme changes or reset is clicked"""
564
+ ctx = callback_context
565
+
566
+ # Determine which input triggered the callback
567
+ if ctx.triggered:
568
+ trigger_id = ctx.triggered[0]['prop_id'].split('.')[0]
569
+ if trigger_id == 'reset-btn' and reset_clicks and reset_clicks > 0:
570
+ # Reset was clicked, use default colors
571
+ colors = VisualizationConfig.DEFAULT_COLORS
572
+ else:
573
+ # Color scheme changed
574
+ colors = VisualizationConfig.COLOR_PRESETS.get(color_scheme, VisualizationConfig.DEFAULT_COLORS)
575
+ else:
576
+ # Initial load
577
+ colors = VisualizationConfig.COLOR_PRESETS.get(color_scheme, VisualizationConfig.DEFAULT_COLORS)
578
+
579
+ return (
580
+ {'hex': colors['Bus']},
581
+ {'hex': colors['Source']},
582
+ {'hex': colors['Sink']},
583
+ {'hex': colors['Storage']},
584
+ {'hex': colors['Converter']},
585
+ )
586
+
587
+ # Updated main visualization callback - simplified logic
538
588
  @app.callback(
539
589
  [Output('cytoscape', 'elements'), Output('cytoscape', 'stylesheet')],
540
590
  [
541
- Input('color-scheme-dropdown', 'value'),
542
591
  Input('bus-color-picker', 'value'),
543
592
  Input('source-color-picker', 'value'),
544
593
  Input('sink-color-picker', 'value'),
@@ -551,7 +600,6 @@ def shownetwork(graph: networkx.DiGraph):
551
600
  [State('elements-store', 'data')],
552
601
  )
553
602
  def update_visualization(
554
- color_scheme,
555
603
  bus_color,
556
604
  source_color,
557
605
  sink_color,
@@ -562,23 +610,20 @@ def shownetwork(graph: networkx.DiGraph):
562
610
  font_size,
563
611
  stored_elements,
564
612
  ):
613
+ """Update visualization based on current color picker values"""
565
614
  if not stored_elements:
566
615
  return no_update, no_update
567
616
 
568
- # Determine colors to use
569
- if any(picker for picker in [bus_color, source_color, sink_color, storage_color, converter_color, edge_color]):
570
- # Use custom colors from pickers
571
- colors = {
572
- 'Bus': bus_color.get('hex') if bus_color else '#7F8C8D',
573
- 'Source': source_color.get('hex') if source_color else '#F1C40F',
574
- 'Sink': sink_color.get('hex') if sink_color else '#F1C40F',
575
- 'Storage': storage_color.get('hex') if storage_color else '#2980B9',
576
- 'Converter': converter_color.get('hex') if converter_color else '#D35400',
577
- 'Other': '#27AE60',
578
- }
579
- else:
580
- # Use preset scheme
581
- colors = VisualizationConfig.COLOR_PRESETS.get(color_scheme, VisualizationConfig.DEFAULT_COLORS)
617
+ # Use colors from pickers (which are now synced with scheme selection)
618
+ default_colors = VisualizationConfig.DEFAULT_COLORS
619
+ colors = {
620
+ 'Bus': bus_color.get('hex') if bus_color else default_colors['Bus'],
621
+ 'Source': source_color.get('hex') if source_color else default_colors['Source'],
622
+ 'Sink': sink_color.get('hex') if sink_color else default_colors['Sink'],
623
+ 'Storage': storage_color.get('hex') if storage_color else default_colors['Storage'],
624
+ 'Converter': converter_color.get('hex') if converter_color else default_colors['Converter'],
625
+ 'Other': default_colors['Other'],
626
+ }
582
627
 
583
628
  # Update element colors
584
629
  updated_elements = []
@@ -681,15 +726,10 @@ def shownetwork(graph: networkx.DiGraph):
681
726
  def update_layout(selected_layout):
682
727
  return {'name': selected_layout}
683
728
 
684
- # Reset callback
729
+ # Reset callback for non-color-picker controls
685
730
  @app.callback(
686
731
  [
687
732
  Output('color-scheme-dropdown', 'value'),
688
- Output('bus-color-picker', 'value'),
689
- Output('source-color-picker', 'value'),
690
- Output('sink-color-picker', 'value'),
691
- Output('storage-color-picker', 'value'),
692
- Output('converter-color-picker', 'value'),
693
733
  Output('edge-color-picker', 'value'),
694
734
  Output('node-size-slider', 'value'),
695
735
  Output('font-size-slider', 'value'),
@@ -698,15 +738,11 @@ def shownetwork(graph: networkx.DiGraph):
698
738
  [Input('reset-btn', 'n_clicks')],
699
739
  )
700
740
  def reset_controls(n_clicks):
741
+ """Reset all controls to defaults (color pickers handled separately)"""
701
742
  if n_clicks and n_clicks > 0:
702
743
  return (
703
- 'Default', # color scheme
704
- {'hex': '#7F8C8D'}, # bus
705
- {'hex': '#F1C40F'}, # source
706
- {'hex': '#F1C40F'}, # sink
707
- {'hex': '#2980B9'}, # storage
708
- {'hex': '#D35400'}, # converter
709
- {'hex': '#808080'}, # edge
744
+ 'Default', # color scheme (will trigger color picker updates)
745
+ {'hex': '#808080'}, # edge color
710
746
  90, # node size
711
747
  10, # font size
712
748
  'klay', # layout