2828import multiprocessing
2929import subprocess
3030
31- try :
32- from PIL import Image , ImageTk
33- PIL_AVAILABLE = True
34- except ImportError :
35- PIL_AVAILABLE = False
36-
37-
38- def _dummy_process_target ():
39- """A picklable top-level function to satisfy multiprocessing on Windows."""
40- pass
41-
42-
4331def launch_new_instance ():
4432 """
4533 Launches a new instance of the plotter application in a separate process.
@@ -49,6 +37,12 @@ def launch_new_instance():
4937 # in the button command for clarity and robustness.
5038 pass
5139
40+ try :
41+ from PIL import Image , ImageTk
42+ PIL_AVAILABLE = True
43+ except ImportError :
44+ PIL_AVAILABLE = False
45+
5246
5347def run_script_process (script_path ):
5448 """
@@ -68,6 +62,7 @@ def run_script_process(script_path):
6862
6963class PlotterAppGUI :
7064 PROGRAM_VERSION = "2.1"
65+ MAX_HEADER_SCAN_LINES = 100
7166 CLR_BG = '#B8A392'
7267 CLR_HEADER = '#E5DCD3'
7368 CLR_FG = '#2C2825'
@@ -107,7 +102,7 @@ def __init__(self, root):
107102
108103 self .active_filepath = None
109104 # New data structure to hold data for multiple files
110- # Format: { "filepath": {"headers": [...], "data": {...}, "mod_time": ..., "size": ...} }
105+ # Format: { "filepath": {"headers": [...], "data": {...}, "mod_time": ..., "size": ..., "delimiter": ... } }
111106 # --- Checkbox UI Change ---
112107 self .file_data_cache = {}
113108 # Stores {filepath: {'var': tk.BooleanVar, 'chk': ttk.Checkbutton,
@@ -802,10 +797,30 @@ def load_file_data(self, filepath):
802797 self .stop_file_watcher ()
803798
804799 try :
805- header_line_index = self ._find_header_row (filepath )
806- data_array = self ._read_data_from_file (filepath , header_line_index )
807- self ._update_cache_and_ui (filepath , data_array )
800+ h_idx , delim , comments = self ._analyze_file_structure (filepath )
801+
802+ if comments :
803+ self .log (f"--- Comments in { os .path .basename (filepath )} ---" )
804+ for c in comments :
805+ self .log (f" { c } " )
806+ self .log ("--- end of comments ---" )
807+
808+ headers , data = self ._read_data_from_file (filepath , h_idx , delim )
809+
810+ file_info = self .file_data_cache [filepath ]
811+ file_info ['headers' ] = headers
812+ file_info ['delimiter' ] = delim
813+ file_info ['data' ] = {h : data [:, j ] for j , h in enumerate (headers )}
814+ file_info ['mod_time' ] = os .path .getmtime (filepath )
815+ file_info ['size' ] = os .path .getsize (filepath )
808816
817+ self .x_col_cb ['values' ] = headers
818+ self .y_col_cb ['values' ] = headers
819+ if len (headers ) > 1 :
820+ self .x_col_cb .set (headers [0 ]); self .y_col_cb .set (headers [1 ])
821+ elif headers :
822+ self .x_col_cb .set (headers [0 ])
823+ self .log (f"Loaded { data .shape [0 ]} points from '{ os .path .basename (filepath )} '." )
809824 except Exception as e :
810825 self ._handle_load_error (filepath , e )
811826 finally :
@@ -820,24 +835,19 @@ def append_file_data(self):
820835 return
821836
822837 self .stop_file_watcher ()
823- file_info = self .file_data_cache .get (self .active_filepath )
824-
825- if not file_info or 'size' not in file_info :
826- self .log (
827- "Cannot append data: file information is incomplete. Performing full reload." )
828- self .load_file_data (self .active_filepath )
829- return
838+ f_info = self .file_data_cache .get (self .active_filepath )
839+ if not f_info or 'size' not in f_info :
840+ self .load_file_data (self .active_filepath ); return
830841
831842 try :
832- new_lines = self ._read_new_lines (self .active_filepath , file_info )
833- appended_count = self ._parse_and_append_new_data (new_lines , file_info )
843+ new_lines = self ._read_new_lines (self .active_filepath , f_info )
844+ appended_count = self ._parse_and_append_new_data (new_lines , f_info )
834845
835846 if appended_count > 0 :
836- file_info ['mod_time' ] = os .path .getmtime (self .active_filepath )
837- file_info ['size' ] = os .path .getsize (self .active_filepath )
847+ f_info ['mod_time' ] = os .path .getmtime (self .active_filepath )
848+ f_info ['size' ] = os .path .getsize (self .active_filepath )
838849 self .log (f"Appended { appended_count } new data points." )
839850 self .plot_data ()
840-
841851 except Exception :
842852 self .log (f"Error appending data: { traceback .format_exc ()} " )
843853 # Fallback to a full reload in case of parsing error
@@ -848,7 +858,7 @@ def append_file_data(self):
848858 def _read_new_lines (self , filepath , file_info ):
849859 """Reads new lines from a file since the last known size."""
850860 try :
851- with open (filepath , 'r' , encoding = 'utf-8' , errors = 'ignore' ) as f :
861+ with open (filepath , 'r' , encoding = 'utf-8-sig ' , errors = 'ignore' ) as f :
852862 f .seek (file_info .get ('size' , 0 ))
853863 new_text = f .read ()
854864
@@ -870,23 +880,23 @@ def _parse_and_append_new_data(self, new_lines, file_info):
870880 if not new_lines :
871881 return 0
872882
873- reader = csv .reader (new_lines )
874- new_data = {h : [] for h in file_info ['headers' ]}
883+ delim = file_info .get ('delimiter' , ',' )
884+ headers = file_info ['headers' ]
885+ new_data = {h : [] for h in headers }
875886 appended_count = 0
876- for row in reader :
877- if len (row ) != len (file_info ['headers' ]):
887+ for line in new_lines :
888+ fields = self ._split_line (line .strip (), delim )
889+ if len (fields ) != len (headers ):
878890 continue
879- for i , header in enumerate ( file_info [ ' headers' ] ):
891+ for h , val in zip ( headers , fields ):
880892 try :
881- new_data [header ].append (float (row [ i ] ))
893+ new_data [h ].append (float (val ))
882894 except (ValueError , TypeError ):
883- new_data [header ].append (np .nan )
895+ new_data [h ].append (np .nan )
884896 appended_count += 1
885897
886- for header in file_info ['headers' ]:
887- file_info ['data' ][header ] = np .concatenate (
888- (file_info ['data' ][header ], np .array (new_data [header ], dtype = float )))
889-
898+ for h in headers :
899+ file_info ['data' ][h ] = np .concatenate ((file_info ['data' ][h ], np .array (new_data [h ], dtype = float )))
890900 return appended_count
891901
892902 def plot_data (self , event = None ):
@@ -964,6 +974,8 @@ def _plot_file_data(self, filepath, x_col, y_col):
964974 linestyle = '-' ,
965975 label = label_text )
966976 return True
977+ else :
978+ self .log (f"Warning: All values in '{ y_col } ' vs '{ x_col } ' for '{ filename } ' are non-finite (inf/NaN) — nothing to plot." )
967979 return False
968980
969981 def _finalize_plot (self , x_col , y_col , selected_filepaths ):
0 commit comments