Skip to content

Commit 7db937c

Browse files
workable_verstion
1 parent b8695ba commit 7db937c

1 file changed

Lines changed: 73 additions & 27 deletions

File tree

pica/utils/PE_plotter.py

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""
22
Module: PE_Plotter_GUI.py
3-
Purpose: A customized P-E Data Plotter extracting specific metadata and overlaying plots.
3+
Purpose: PE Hysteresis Plotter Utility (Part of PICA suite).
44
"""
55

66
import tkinter as tk
7-
from tkinter import ttk, filedialog, messagebox, scrolledtext
7+
from tkinter import ttk, filedialog, messagebox, scrolledtext, Canvas
88
import os
99
import io
1010
import pandas as pd
@@ -14,10 +14,17 @@
1414
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
1515
import matplotlib as mpl
1616

17+
try:
18+
from PIL import Image, ImageTk
19+
PIL_AVAILABLE = True
20+
except ImportError:
21+
PIL_AVAILABLE = False
22+
23+
1724
class PEPlotterAppGUI:
18-
PROGRAM_VERSION = "1.0"
25+
PROGRAM_VERSION = "2.2"
1926

20-
# --- Styling to match your template ---
27+
# --- PICA Styling ---
2128
CLR_BG = '#B8A392'
2229
CLR_HEADER = '#E5DCD3'
2330
CLR_FG = '#2C2825'
@@ -30,21 +37,28 @@ class PEPlotterAppGUI:
3037
FONT_BASE = ('Segoe UI', 10)
3138
FONT_TITLE = ('Segoe UI', 12, 'bold')
3239

40+
try:
41+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
42+
LOGO_FILE_PATH = os.path.join(SCRIPT_DIR, "..", "assets", "LOGO", "UGC_DAE_CSR_NBG.jpeg")
43+
except NameError:
44+
LOGO_FILE_PATH = "../assets/LOGO/UGC_DAE_CSR_NBG.jpeg"
45+
3346
def __init__(self, root):
3447
self.root = root
35-
self.root.title(f"P-E Data Plotter v{self.PROGRAM_VERSION}")
36-
self.root.geometry("1300x800")
37-
self.root.minsize(1000, 600)
48+
self.root.title(f"PICA PE Plotter Utility v{self.PROGRAM_VERSION}")
49+
self.root.geometry("1400x900")
50+
self.root.minsize(1100, 700)
3851
self.root.configure(bg=self.CLR_BG)
3952

4053
self.active_filepath = None
4154
self.file_data_cache = {} # {filepath: {'df': dataframe, 'metadata': dict}}
4255
self.file_ui_elements = {} # {filepath: {'var': boolVar, 'chk': checkbutton, 'lbl': label, 'frame': frame}}
56+
self.logo_image = None
4357

4458
self.setup_styles()
4559
self.create_widgets()
46-
self.log("Welcome to the P-E Plotter Utility.")
47-
self.log("Click 'Add File(s)...' to load your .txt or .csv files.")
60+
self.log("Welcome to the PICA PE Hysteresis Plotter Utility.")
61+
self.log("Click 'Add File(s)...' to load your .txt or .csv measurement files.")
4862

4963
def setup_styles(self):
5064
self.style = ttk.Style(self.root)
@@ -55,33 +69,66 @@ def setup_styles(self):
5569
self.style.configure('TLabel', background=self.CLR_FRAME_BG, foreground=self.CLR_FG)
5670
self.style.configure('Header.TLabel', background=self.CLR_HEADER)
5771
self.style.configure('TButton', font=self.FONT_BASE, padding=(8, 5), foreground=self.CLR_ACCENT_GOLD, background=self.CLR_HEADER)
58-
self.style.configure('Plot.TButton', background=self.CLR_ACCENT_GREEN, foreground=self.CLR_BG)
72+
self.style.map('TButton', background=[('active', self.CLR_ACCENT_GOLD), ('hover', self.CLR_ACCENT_GOLD)],
73+
foreground=[('active', self.CLR_BG), ('hover', self.CLR_BG)])
5974
self.style.map('TCombobox', fieldbackground=[('readonly', self.CLR_INPUT_BG)])
6075
self.style.configure('TLabelframe', background=self.CLR_FRAME_BG, bordercolor=self.CLR_ACCENT_BLUE)
6176
self.style.configure('TLabelframe.Label', background=self.CLR_FRAME_BG, foreground=self.CLR_FG, font=self.FONT_TITLE)
6277
self.style.configure('Input.TFrame', background=self.CLR_INPUT_BG)
6378

6479
mpl.rcParams.update({
65-
'font.family': 'Segoe UI', 'font.size': 10,
80+
'font.family': 'Segoe UI', 'font.size': 11,
81+
'axes.titlesize': 14, 'axes.labelsize': 12,
6682
'figure.facecolor': self.CLR_BG, 'axes.facecolor': '#F4EFEA',
6783
'axes.edgecolor': self.CLR_FG, 'axes.labelcolor': self.CLR_FG,
68-
'text.color': self.CLR_FG,
84+
'text.color': self.CLR_FG, 'xtick.color': self.CLR_FG, 'ytick.color': self.CLR_FG
6985
})
7086

7187
def create_widgets(self):
72-
# --- Header ---
88+
# --- Header (PICA Style with Logo and Institute Name) ---
7389
header = tk.Frame(self.root, bg=self.CLR_HEADER)
7490
header.pack(side='top', fill='x', padx=1, pady=1)
75-
ttk.Label(header, text="P-E Hysteresis Plotter", style='Header.TLabel', font=('Segoe UI', 16, 'bold')).pack(side='left', padx=20, pady=15)
91+
header.grid_columnconfigure(1, weight=1)
92+
93+
left_header_frame = tk.Frame(header, bg=self.CLR_HEADER)
94+
left_header_frame.grid(row=0, column=0, sticky='w')
95+
font_title_main = ('Segoe UI', 14, 'bold')
96+
97+
ttk.Label(left_header_frame, text="PE Hysteresis Plotter Utility", style='Header.TLabel',
98+
font=font_title_main, foreground=self.CLR_ACCENT_GOLD).pack(side='top', anchor='w', padx=20, pady=(10, 0))
99+
ttk.Label(left_header_frame, text="(Part of the PICA Suite)", style='Header.TLabel',
100+
font=('Segoe UI', 10, 'italic'), foreground=self.CLR_FG).pack(side='top', anchor='w', padx=20, pady=(0, 10))
76101

102+
center_header_frame = tk.Frame(header, bg=self.CLR_HEADER)
103+
center_header_frame.grid(row=0, column=1, sticky='ew')
104+
105+
logo_canvas = Canvas(center_header_frame, width=60, height=60, bg=self.CLR_HEADER, highlightthickness=0)
106+
logo_canvas.pack(side='left', pady=10)
107+
108+
if PIL_AVAILABLE and os.path.exists(self.LOGO_FILE_PATH):
109+
try:
110+
img = Image.open(self.LOGO_FILE_PATH).resize((60, 60), Image.Resampling.LANCZOS)
111+
self.logo_image = ImageTk.PhotoImage(img)
112+
logo_canvas.create_image(30, 30, image=self.logo_image)
113+
except Exception as e:
114+
self.log(f"Warning: Could not load logo. {e}")
115+
116+
institute_frame = tk.Frame(center_header_frame, bg=self.CLR_HEADER)
117+
institute_frame.pack(side='left', padx=15)
118+
ttk.Label(institute_frame, text="UGC-DAE Consortium for Scientific Research",
119+
style='Header.TLabel', font=('Segoe UI', 16, 'bold')).pack(anchor='w')
120+
ttk.Label(institute_frame, text="Mumbai Centre",
121+
style='Header.TLabel', font=('Segoe UI', 14)).pack(anchor='w')
122+
123+
# --- Main Layout ---
77124
main_pane = ttk.PanedWindow(self.root, orient='horizontal')
78125
main_pane.pack(fill='both', expand=True, padx=10, pady=10)
79126

80127
left_panel = self._create_left_panel(main_pane)
81128
main_pane.add(left_panel, weight=1)
82129

83130
right_panel = self._create_right_panel(main_pane)
84-
main_pane.add(right_panel, weight=3)
131+
main_pane.add(right_panel, weight=4) # Higher weight to ensure plot takes most space
85132

86133
def _create_left_panel(self, parent):
87134
panel = ttk.Frame(parent, width=350)
@@ -102,7 +149,7 @@ def _create_left_panel(self, parent):
102149
list_container = ttk.Frame(file_frame, style='TFrame')
103150
list_container.grid(row=1, column=0, sticky='nsew', padx=10, pady=(0, 10))
104151

105-
file_canvas = tk.Canvas(list_container, bg=self.CLR_INPUT_BG, highlightthickness=0, height=120)
152+
file_canvas = tk.Canvas(list_container, bg=self.CLR_INPUT_BG, highlightthickness=0, height=150)
106153
scrollbar = ttk.Scrollbar(list_container, orient="vertical", command=file_canvas.yview)
107154
self.file_list_frame = ttk.Frame(file_canvas, style='Input.TFrame')
108155

@@ -137,6 +184,7 @@ def _create_left_panel(self, parent):
137184

138185
def _create_right_panel(self, parent):
139186
panel = ttk.Frame(parent)
187+
# Using pack to make sure it fills the complete right side seamlessly
140188
container = ttk.LabelFrame(panel, text='Visualization')
141189
container.pack(fill='both', expand=True)
142190

@@ -186,7 +234,6 @@ def parse_file_custom(self, filepath):
186234
line_clean = line.strip()
187235
if not line_clean: continue
188236

189-
# Extract Metadata
190237
if 'Sample Area' in line_clean:
191238
metadata['Sample Area'] = line_clean.split(':')[-1].strip()
192239
elif 'Sample Thickness' in line_clean:
@@ -204,21 +251,18 @@ def parse_file_custom(self, filepath):
204251
elif 'Stored:' in line_clean and metadata['Measurement Date'] == 'Unknown':
205252
metadata['Measurement Date'] = line_clean.split('Stored:')[-1].strip()
206253

207-
# Identify Headers
208254
if 'Point' in line_clean and ('Time' in line_clean or 'Drive' in line_clean):
209255
sep = ',' if ',' in line_clean else '\t'
210256
headers = [h.strip() for h in line_clean.split(sep)]
211257
is_data = True
212258
continue
213259

214-
# Collect Data
215260
if is_data:
216261
sep = ',' if ',' in line_clean else '\t'
217262
parts = line_clean.split(sep)
218263
if len(parts) >= 3:
219264
data_lines.append(line_clean)
220265

221-
# Build DataFrame
222266
if data_lines:
223267
sep = ',' if filepath.lower().endswith('.csv') else '\t'
224268
df = pd.read_csv(io.StringIO('\n'.join(data_lines)), sep=sep, header=None)
@@ -247,11 +291,10 @@ def browse_files(self):
247291
self.file_data_cache[fp] = {'df': df, 'metadata': metadata}
248292
self._add_file_to_ui(fp)
249293

250-
# Print metadata to console
251294
self.log(f"Loaded: {os.path.basename(fp)}")
252295
self.log(f" Date: {metadata['Measurement Date']}")
253-
self.log(f" Area: {metadata['Sample Area']} cm2 | Thickness: {metadata['Sample Thickness']} um")
254-
self.log(f" Voltage: {metadata['Applied Voltage']} V | Frequency: {metadata['Frequency (Hz)']} Hz\n")
296+
self.log(f" Area: {metadata['Sample Area']} cm2 | Thick: {metadata['Sample Thickness']} um")
297+
self.log(f" Voltage: {metadata['Applied Voltage']} V | Freq: {metadata['Frequency (Hz)']} Hz\n")
255298

256299
self._update_dropdowns(df.columns.tolist())
257300

@@ -290,7 +333,6 @@ def _update_dropdowns(self, columns):
290333
self.x_col_cb['values'] = columns
291334
self.y_col_cb['values'] = columns
292335

293-
# Intelligent Defaults for P-E loop
294336
if not current_x:
295337
default_x = next((c for c in columns if 'Voltage' in c or 'Field' in c), columns[0])
296338
self.x_col_cb.set(default_x)
@@ -320,14 +362,18 @@ def plot_data(self, event=None):
320362

321363
self.ax_main.set_xlabel(x_col, fontweight='bold')
322364
self.ax_main.set_ylabel(y_col, fontweight='bold')
323-
self.ax_main.set_title("Overlay P-E Hysteresis Plot", fontweight='bold')
365+
self.ax_main.set_title("PE Hysteresis Overlay", fontweight='bold')
366+
367+
# Placing legend *inside* the plot area (loc='best' finds the clearest corner automatically)
368+
self.ax_main.legend(title="Sample Files", loc='best')
324369

325-
# Legend
326-
self.ax_main.legend(title="Sample Files", bbox_to_anchor=(1.01, 1), loc='upper left')
327370
self.figure.tight_layout()
328371
self.canvas.draw_idle()
329372

373+
330374
if __name__ == '__main__':
375+
import multiprocessing
376+
multiprocessing.freeze_support()
331377
root = tk.Tk()
332378
app = PEPlotterAppGUI(root)
333379
root.mainloop()

0 commit comments

Comments
 (0)