Skip to content

Commit b435aab

Browse files
2400 V T GUI updated
1 parent 4cb320f commit b435aab

1 file changed

Lines changed: 50 additions & 36 deletions

File tree

Keithley_2400/Frontend_Keithley_2400_Lakeshore_350_V_vs_T_V1.py

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# -------------------------------------------------------------------------------
22
# Name: Temperature Dependent I-V Measurement GUI
33
# Purpose: Provide a professional graphical user interface for controlling
4-
# a Lakeshore 350 and a Keithley 2400.
4+
# a Lakeshore 350 and a Keithley 2400 for automated V-T sweeps.
55
# Author: Prathamesh Deshmukh
66
# Created: 18/09/2025
7-
# Version: 5.2 (Original Theme Restored)
7+
# Version: 6.0 (Modernized GUI)
88
# -------------------------------------------------------------------------------
99

1010
# --- GUI and Plotting Packages ---
@@ -16,6 +16,7 @@
1616
import traceback
1717
from datetime import datetime
1818
from matplotlib.figure import Figure
19+
import matplotlib.gridspec as gridspec
1920
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
2021
import matplotlib as mpl
2122

@@ -98,15 +99,16 @@ def shutdown_all(self):
9899
# FRONTEND CLASS - The Main GUI Application
99100
#===============================================================================
100101
class TemperatureIVGUI:
101-
PROGRAM_VERSION = "5.2"
102-
# --- Original Theme Colors Restored ---
103-
CLR_BG, CLR_HEADER, CLR_FG = '#2B3D4F', '#3A506B', '#EDF2F4'
104-
CLR_FRAME_BG, CLR_INPUT_BG = '#3A506B', '#4C566A'
105-
CLR_ACCENT_GREEN, CLR_ACCENT_RED, CLR_ACCENT_BLUE = '#A7C957', '#EF233C', '#8D99AE'
102+
PROGRAM_VERSION = "6.0"
103+
# --- Modern PICA Theme ---
104+
CLR_BG = '#2B3D4F'; CLR_HEADER = '#3A506B'; CLR_FG = '#EDF2F4'
105+
CLR_FRAME_BG = '#3A506B'; CLR_INPUT_BG = '#4C566A'
106+
CLR_ACCENT_GREEN, CLR_ACCENT_RED, CLR_ACCENT_BLUE = '#A7C957', '#E74C3C', '#8D99AE'
107+
CLR_ACCENT_GOLD = '#FFC107'
106108
CLR_CONSOLE_BG = '#1E2B38'
107-
FONT_BASE = ('Segoe UI', 10); FONT_TITLE = ('Segoe UI', 12, 'bold')
109+
FONT_BASE = ('Segoe UI', 11); FONT_TITLE = ('Segoe UI', 13, 'bold')
108110
try:
109-
# Robust path finding for assets
111+
# Robust path finding for assets, assuming standard PICA structure
110112
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
111113
LOGO_FILE = os.path.join(SCRIPT_DIR, "..", "_assets", "LOGO", "UGC_DAE_CSR.jpeg")
112114
except NameError:
@@ -119,7 +121,7 @@ def __init__(self, root):
119121
self.root.geometry("1600x950")
120122
self.root.configure(bg=self.CLR_BG)
121123
self.root.minsize(1400, 800)
122-
self.backend = ExperimentBackend(); self.experiment_state = 'idle'
124+
self.backend = ExperimentBackend(); self.experiment_state = 'idle'; self.logo_image = None
123125
self.setup_styles()
124126
self.create_widgets()
125127
self.root.protocol("WM_DELETE_WINDOW", self._on_closing)
@@ -129,20 +131,24 @@ def setup_styles(self):
129131
style = ttk.Style(self.root)
130132
style.theme_use('clam')
131133
style.configure('.', background=self.CLR_BG, foreground=self.CLR_FG, font=self.FONT_BASE, bordercolor=self.CLR_ACCENT_BLUE)
132-
style.configure('TFrame', background=self.CLR_BG)
133-
style.configure('TPanedWindow', background=self.CLR_BG)
134-
style.configure('TLabel', background=self.CLR_FRAME_BG, foreground=self.CLR_FG)
135-
style.configure('Header.TLabel', background=self.CLR_HEADER)
134+
style.configure('TFrame', background=self.CLR_BG); style.configure('TPanedWindow', background=self.CLR_BG)
135+
style.configure('TLabel', background=self.CLR_FRAME_BG, foreground=self.CLR_FG); style.configure('Header.TLabel', background=self.CLR_HEADER)
136136
style.configure('TEntry', fieldbackground=self.CLR_INPUT_BG, foreground=self.CLR_FG, bordercolor=self.CLR_ACCENT_BLUE, insertcolor=self.CLR_FG)
137137
style.map('TEntry', background=[('active', self.CLR_INPUT_BG), ('!disabled', self.CLR_INPUT_BG)])
138-
style.configure('TButton', padding=(10, 8), background=self.CLR_ACCENT_BLUE, foreground=self.CLR_BG)
139-
style.map('TButton', background=[('active', self.CLR_FG), ('!disabled', self.CLR_ACCENT_BLUE)], foreground=[('active', self.CLR_BG)])
140-
style.configure('Start.TButton', background=self.CLR_ACCENT_GREEN)
141-
style.configure('Stop.TButton', background=self.CLR_ACCENT_RED)
138+
139+
style.configure('TButton', font=self.FONT_BASE, padding=(10, 9), foreground=self.CLR_ACCENT_GOLD, background=self.CLR_HEADER, borderwidth=0, focusthickness=0, focuscolor='none')
140+
style.map('TButton', background=[('active', self.CLR_ACCENT_GOLD), ('hover', self.CLR_ACCENT_GOLD)], foreground=[('active', self.CLR_BG), ('hover', self.CLR_BG)])
141+
142+
style.configure('Start.TButton', font=self.FONT_BASE, padding=(10, 9), background=self.CLR_ACCENT_GREEN, foreground=self.CLR_BG)
143+
style.map('Start.TButton', background=[('active', '#8AB845'), ('hover', '#8AB845')])
144+
145+
style.configure('Stop.TButton', font=self.FONT_BASE, padding=(10, 9), background=self.CLR_ACCENT_RED, foreground=self.CLR_FG)
146+
style.map('Stop.TButton', background=[('active', '#D63C2A'), ('hover', '#D63C2A')])
147+
142148
style.map('TButton', foreground=[('disabled', '#6e7a91')])
143149
style.configure('TLabelframe', background=self.CLR_FRAME_BG, bordercolor=self.CLR_ACCENT_BLUE)
144150
style.configure('TLabelframe.Label', background=self.CLR_FRAME_BG, foreground=self.CLR_FG, font=self.FONT_TITLE)
145-
mpl.rcParams['font.family'] = 'Segoe UI'
151+
mpl.rcParams.update({'font.family': 'Segoe UI', 'font.size': 11, 'axes.titlesize': 15, 'axes.labelsize': 13})
146152

147153
def create_widgets(self):
148154
self.create_header()
@@ -165,13 +171,18 @@ def _create_right_panel(self, parent):
165171
panel = ttk.Frame(parent, padding=5)
166172
container = ttk.LabelFrame(panel, text='Live I-V Curve')
167173
container.pack(fill='both', expand=True)
168-
self.figure = Figure(dpi=100, facecolor='white'); self.ax_main = self.figure.add_subplot(111)
169-
self.ax_main.set_facecolor('#f0f0f0')
170-
self.ax_main.grid(True, linestyle='--', color='white'); self.ax_main.axhline(0, color='k', lw=0.5); self.ax_main.axvline(0, color='k', lw=0.5)
174+
self.figure = Figure(dpi=100, facecolor='white')
175+
gs = gridspec.GridSpec(2, 1, figure=self.figure, height_ratios=[3, 1])
176+
self.ax_main = self.figure.add_subplot(gs[0, 0])
177+
self.ax_temp = self.figure.add_subplot(gs[1, 0], sharex=self.ax_main)
178+
171179
self.line_main, = self.ax_main.plot([], [], color=self.CLR_ACCENT_RED, marker='o', markersize=3, linestyle='-')
172-
self.ax_main.set_title("Waiting for experiment...", fontweight='bold', color=self.CLR_BG)
173-
self.ax_main.set_xlabel("Current (A)", color=self.CLR_BG); self.ax_main.set_ylabel("Voltage (V)", color=self.CLR_BG)
174-
self.ax_main.tick_params(colors=self.CLR_BG); self.figure.tight_layout()
180+
self.ax_main.set_title("Waiting for experiment...", fontweight='bold'); self.ax_main.set_ylabel("Voltage (V)")
181+
self.ax_main.tick_params(axis='x', labelbottom=False)
182+
self.line_temp, = self.ax_temp.plot([], [], color=self.CLR_ACCENT_BLUE, marker='.', markersize=3, linestyle='-')
183+
self.ax_temp.set_xlabel("Current (A)"); self.ax_temp.set_ylabel("Temperature (K)")
184+
for ax in [self.ax_main, self.ax_temp]: ax.grid(True, linestyle='--', alpha=0.6)
185+
self.figure.tight_layout()
175186
self.canvas = FigureCanvasTkAgg(self.figure, container); self.canvas.get_tk_widget().pack(fill='both', expand=True, padx=5, pady=5)
176187
return panel
177188

@@ -184,13 +195,13 @@ def create_header(self):
184195
def _create_info_panel(self, parent, grid_row):
185196
frame = ttk.LabelFrame(parent, text='Information')
186197
frame.grid(row=grid_row, column=0, sticky='new', pady=5)
187-
frame.grid_columnconfigure(1, weight=1)
188-
logo_canvas = tk.Canvas(frame, width=80, height=80, bg=self.CLR_FRAME_BG, highlightthickness=0)
198+
frame.grid_columnconfigure(1, weight=1); logo_canvas = tk.Canvas(frame, width=80, height=80, bg=self.CLR_FRAME_BG, highlightthickness=0)
189199
logo_canvas.grid(row=0, column=0, padx=10, pady=10)
190200
if PIL_AVAILABLE and os.path.exists(self.LOGO_FILE):
191201
try:
192202
img = Image.open(self.LOGO_FILE).resize((80, 80), Image.Resampling.LANCZOS)
193-
self.logo_image = ImageTk.PhotoImage(img); logo_canvas.create_image(40, 40, image=self.logo_image)
203+
self.logo_image = ImageTk.PhotoImage(img)
204+
logo_canvas.create_image(40, 40, image=self.logo_image)
194205
except Exception: pass
195206
info_text = ("Institute: UGC DAE CSR, Mumbai\nMeasurement: Voltage vs. Temperature\nInstruments: Lakeshore 350 & Keithley 2400")
196207
ttk.Label(frame, text=info_text, justify='left').grid(row=0, column=1, sticky='w', padx=5)
@@ -203,15 +214,13 @@ def _create_params_panel(self, parent, grid_row):
203214
temp_frame = ttk.LabelFrame(container, text='Temperature')
204215
temp_frame.grid(row=0, column=0, sticky='nsew', padx=(0,5))
205216
temp_frame.grid_columnconfigure(1, weight=1)
206-
self._create_entry(temp_frame, "Start Temp (K)", "300", 0)
207-
self._create_entry(temp_frame, "End Temp (K)", "280", 1)
217+
self._create_entry(temp_frame, "Start Temp (K)", "300", 0); self._create_entry(temp_frame, "End Temp (K)", "280", 1)
208218
self._create_entry(temp_frame, "Temp Step (K)", "-5", 2)
209219
self.lakeshore_combobox = self._create_combobox(temp_frame, "Lakeshore VISA", 3)
210220
iv_frame = ttk.LabelFrame(container, text='I-V Sweep')
211221
iv_frame.grid(row=0, column=1, sticky='nsew', padx=(5,0))
212222
iv_frame.grid_columnconfigure(1, weight=1)
213-
self._create_entry(iv_frame, "Max Current (µA)", "100", 0)
214-
self._create_entry(iv_frame, "Step Current (µA)", "5", 1)
223+
self._create_entry(iv_frame, "Max Current (µA)", "100", 0); self._create_entry(iv_frame, "Step Current (µA)", "5", 1)
215224
self._create_entry(iv_frame, "Compliance (V)", "10", 2)
216225
self._create_entry(iv_frame, "Dwell Time (s)", "0.2", 3)
217226
self.keithley_combobox = self._create_combobox(iv_frame, "Keithley VISA", 4)
@@ -277,15 +286,17 @@ def _experiment_loop(self):
277286
self.log(f"Temperature stabilized at {temp_now:.4f} K."); self.experiment_state = 'run_sweep'
278287
self.root.after(100, self._experiment_loop)
279288
else:
280-
self.root.after(5000, self._experiment_loop)
289+
self.root.after(3000, self._experiment_loop)
281290
elif self.experiment_state == 'run_sweep':
282291
temp_now = self.backend.get_temperature()
283292
self.log(f"Starting I-V sweep at {temp_now:.4f} K...")
284293
self.ax_main.set_title(f"Sweeping at {temp_now:.2f} K..."); self.line_main.set_data([], []); self.canvas.draw()
285294
currents, voltages = self.backend.run_iv_sweep(self._validate_and_get_params())
286295
self.log("I-V sweep complete."); self.line_main.set_data(currents, voltages)
287296
self.ax_main.relim(); self.ax_main.autoscale_view(); self.canvas.draw()
288-
self._save_data(currents, voltages, temp_now)
297+
temps_for_plot = np.full_like(currents, temp_now)
298+
self.line_temp.set_data(currents, temps_for_plot); self.ax_temp.relim(); self.ax_temp.autoscale_view()
299+
self._save_data(currents, voltages, temp_now); self.canvas.draw()
289300
self.current_temp_index += 1
290301
if self.current_temp_index >= len(self.temp_points):
291302
self.stop_experiment("All points measured.")
@@ -323,7 +334,7 @@ def set_ui_state(self, running: bool):
323334
self.stop_button.config(state='normal' if running else 'disabled')
324335

325336
def _scan_for_visa_instruments(self):
326-
if self.backend.rm is None: self.log("ERROR: VISA library missing."); return
337+
if self.backend.rm is None: self.log("ERROR: PyVISA library missing or failed to load."); return
327338
self.log("Scanning for VISA instruments..."); resources = self.backend.rm.list_resources()
328339
if resources:
329340
self.log(f"Found: {resources}"); self.lakeshore_combobox['values'] = resources; self.keithley_combobox['values'] = resources
@@ -334,7 +345,8 @@ def _scan_for_visa_instruments(self):
334345

335346
def _browse_file_location(self):
336347
path = filedialog.askdirectory()
337-
if path: self.entries["Save Location"].delete(0, 'end'); self.entries["Save Location"].insert(0, path)
348+
if path:
349+
self.entries["Save Location"].config(state='normal'); self.entries["Save Location"].delete(0, 'end'); self.entries["Save Location"].insert(0, path); self.entries["Save Location"].config(state='disabled')
338350

339351
def _create_entry(self, parent, label_text, default_value, row, browse=False):
340352
ttk.Label(parent, text=f"{label_text}:").grid(row=row, column=0, sticky='w', padx=10, pady=3)
@@ -344,6 +356,8 @@ def _create_entry(self, parent, label_text, default_value, row, browse=False):
344356
if browse:
345357
btn = ttk.Button(parent, text="...", width=3, command=self._browse_file_location)
346358
btn.grid(row=row, column=3, sticky='e', padx=(0,10))
359+
# Make save location entry read-only to enforce using the button
360+
entry.config(state='disabled')
347361

348362
def _create_combobox(self, parent, label_text, row):
349363
ttk.Label(parent, text=f"{label_text}:").grid(row=row, column=0, sticky='w', padx=10, pady=3)

0 commit comments

Comments
 (0)