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+
3143def launch_new_instance ():
3244 """
3345 Launches a new instance of the plotter application in a separate process.
@@ -37,12 +49,6 @@ def launch_new_instance():
3749 # in the button command for clarity and robustness.
3850 pass
3951
40- try :
41- from PIL import Image , ImageTk
42- PIL_AVAILABLE = True
43- except ImportError :
44- PIL_AVAILABLE = False
45-
4652
4753def run_script_process (script_path ):
4854 """
@@ -62,7 +68,6 @@ def run_script_process(script_path):
6268
6369class PlotterAppGUI :
6470 PROGRAM_VERSION = "2.1"
65- MAX_HEADER_SCAN_LINES = 100
6671 CLR_BG = '#B8A392'
6772 CLR_HEADER = '#E5DCD3'
6873 CLR_FG = '#2C2825'
@@ -102,7 +107,7 @@ def __init__(self, root):
102107
103108 self .active_filepath = None
104109 # New data structure to hold data for multiple files
105- # Format: { "filepath": {"headers": [...], "data": {...}, "mod_time": ..., "size": ..., "delimiter": ... } }
110+ # Format: { "filepath": {"headers": [...], "data": {...}, "mod_time": ..., "size": ...} }
106111 # --- Checkbox UI Change ---
107112 self .file_data_cache = {}
108113 # Stores {filepath: {'var': tk.BooleanVar, 'chk': ttk.Checkbutton,
@@ -797,30 +802,10 @@ def load_file_data(self, filepath):
797802 self .stop_file_watcher ()
798803
799804 try :
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 )
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 )
816808
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 )} '." )
824809 except Exception as e :
825810 self ._handle_load_error (filepath , e )
826811 finally :
@@ -835,19 +820,24 @@ def append_file_data(self):
835820 return
836821
837822 self .stop_file_watcher ()
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
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
841830
842831 try :
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 )
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 )
845834
846835 if appended_count > 0 :
847- f_info ['mod_time' ] = os .path .getmtime (self .active_filepath )
848- f_info ['size' ] = os .path .getsize (self .active_filepath )
836+ file_info ['mod_time' ] = os .path .getmtime (self .active_filepath )
837+ file_info ['size' ] = os .path .getsize (self .active_filepath )
849838 self .log (f"Appended { appended_count } new data points." )
850839 self .plot_data ()
840+
851841 except Exception :
852842 self .log (f"Error appending data: { traceback .format_exc ()} " )
853843 # Fallback to a full reload in case of parsing error
@@ -858,7 +848,7 @@ def append_file_data(self):
858848 def _read_new_lines (self , filepath , file_info ):
859849 """Reads new lines from a file since the last known size."""
860850 try :
861- with open (filepath , 'r' , encoding = 'utf-8-sig ' , errors = 'ignore' ) as f :
851+ with open (filepath , 'r' , encoding = 'utf-8' , errors = 'ignore' ) as f :
862852 f .seek (file_info .get ('size' , 0 ))
863853 new_text = f .read ()
864854
@@ -880,23 +870,23 @@ def _parse_and_append_new_data(self, new_lines, file_info):
880870 if not new_lines :
881871 return 0
882872
883- delim = file_info .get ('delimiter' , ',' )
884- headers = file_info ['headers' ]
885- new_data = {h : [] for h in headers }
873+ reader = csv .reader (new_lines )
874+ new_data = {h : [] for h in file_info ['headers' ]}
886875 appended_count = 0
887- for line in new_lines :
888- fields = self ._split_line (line .strip (), delim )
889- if len (fields ) != len (headers ):
876+ for row in reader :
877+ if len (row ) != len (file_info ['headers' ]):
890878 continue
891- for h , val in zip ( headers , fields ):
879+ for i , header in enumerate ( file_info [ ' headers' ] ):
892880 try :
893- new_data [h ].append (float (val ))
881+ new_data [header ].append (float (row [ i ] ))
894882 except (ValueError , TypeError ):
895- new_data [h ].append (np .nan )
883+ new_data [header ].append (np .nan )
896884 appended_count += 1
897885
898- for h in headers :
899- file_info ['data' ][h ] = np .concatenate ((file_info ['data' ][h ], np .array (new_data [h ], dtype = float )))
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+
900890 return appended_count
901891
902892 def plot_data (self , event = None ):
@@ -974,8 +964,6 @@ def _plot_file_data(self, filepath, x_col, y_col):
974964 linestyle = '-' ,
975965 label = label_text )
976966 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." )
979967 return False
980968
981969 def _finalize_plot (self , x_col , y_col , selected_filepaths ):
@@ -1080,4 +1068,4 @@ def check_for_updates(self):
10801068
10811069 root = tk .Tk ()
10821070 app = PlotterAppGUI (root )
1083- root .mainloop ()
1071+ root .mainloop ()
0 commit comments