Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Measurements/AMR/amr_GUI.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im assuming the live_plot= false turns off the second window popping up?

Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ def run_measurement(self):
frequency=frequency,
measure_time=measure_time,
sensitivity=sensitivity,
save_dir=save_dir
save_dir=save_dir,
live_plot=False,
)

self.is_measuring = True
Expand Down
109 changes: 99 additions & 10 deletions Measurements/DCIV/IV_sweep_GUI.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it would be nice to have the same pause or stop capability of AMR, also by chance could we make the returned data more than just a flat line, assume you have like a resistor or something connected so it shows like a line or something

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,55 @@
import tkinter as tk
from tkinter import ttk
import numpy as np
import threading
import time
import pandas as pd
from piec.drivers.sourcemeter.keithley2400 import Keithley2400
from piec.drivers.sourcemeter.virtual_keithley2400 import VirtualKeithley2400
from piec.measurement.iv_sweep import IVSweep
from piec.analysis.utilities import standard_csv_to_metadata_and_data
from piec.measurement.gui_utils import MeasurementApp


class _LiveIVSweep(IVSweep):
"""IVSweep subclass that tracks data live for GUI updates.

Uses underscore-prefixed attributes so _update_metadata() ignores them.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._live_voltages = []
self._live_currents = []

def sweep(self):
voltages = np.linspace(self.v_start, self.v_stop, self.num_steps)
measured_voltages = []
measured_currents = []
self._live_voltages = []
self._live_currents = []

print(f"Starting IV sweep: {self.v_start}V to {self.v_stop}V in {self.num_steps} steps...")
self.sourcemeter.output(on=True)

for i, v in enumerate(voltages):
self.sourcemeter.set_source_voltage(v)
time.sleep(self.dwell_time)
measured_v = self.sourcemeter.get_voltage()
measured_i = self.sourcemeter.get_current()
measured_voltages.append(measured_v)
measured_currents.append(measured_i)
self._live_voltages.append(measured_v)
self._live_currents.append(measured_i)

if (i + 1) % max(1, self.num_steps // 10) == 0:
print(f" Step {i + 1}/{self.num_steps}: V={measured_v:.4f} V, I={measured_i:.6e} A")

self.data = pd.DataFrame({
"voltage (V)": measured_voltages,
"current (A)": measured_currents,
})
print("Sweep complete.")

DEFAULTS = {
"sm_address": "VIRTUAL",
"save_dir": r"your\default\save\directory",
Expand All @@ -28,6 +71,9 @@ def __init__(self, root):
print("Welcome to the IV Sweep GUI!")
print("Ctrl+Enter: Run Measurement")

self.measurement_thread = None
self.is_measuring = False

visa_resources = self.get_visa_resources()

# Static Inputs (Save Dir is at row 0 in base)
Expand Down Expand Up @@ -109,6 +155,10 @@ def refresh_instruments(self):
self.sm_address_entry.set("VIRTUAL")

def run_measurement(self):
if self.is_measuring:
print("Measurement already in progress...")
return

print("Running IV Sweep measurement...")

sm_address = self.sm_address_entry.get()
Expand All @@ -134,7 +184,7 @@ def run_measurement(self):
else:
sourcemeter = Keithley2400(sm_address)

self.experiment = IVSweep(
self.experiment = _LiveIVSweep(
sourcemeter=sourcemeter,
v_start=v_start,
v_stop=v_stop,
Expand All @@ -144,18 +194,57 @@ def run_measurement(self):
sense_mode=sense_mode,
save_dir=save_dir,
)
self.experiment.run_experiment()

self.is_measuring = True
self.run_button.config(state='disabled')

self.measurement_thread = threading.Thread(
target=self.experiment.run_experiment,
daemon=True,
)
self.measurement_thread.start()
self.update_plot_loop()

def update_plot_loop(self):
if not self.is_measuring:
return

self.plot_data()

if self.measurement_thread and self.measurement_thread.is_alive():
self.root.after(500, self.update_plot_loop)
else:
self.is_measuring = False
self.run_button.config(state='normal')
print("Measurement complete.")
self.plot_data()

def plot_data(self, event=None):
self.ax.clear()
metadata, data = standard_csv_to_metadata_and_data(self.experiment.filename)
x_data = data[self.x_axis.get()]
y_data = data[self.y_axis.get()]
if not hasattr(self, 'experiment'):
return

self.ax.plot(x_data, y_data, marker=".", color="k", label=f"{self.y_axis.get()} vs {self.x_axis.get()}")
self.ax.set_xlabel(self.x_axis.get())
self.ax.set_ylabel(self.y_axis.get())
live_v = self.experiment._live_voltages
live_i = self.experiment._live_currents

if not live_v:
return

live_data = {
"voltage (V)": live_v,
"current (A)": live_i,
}

x_col = self.x_axis.get()
y_col = self.y_axis.get()

if x_col not in live_data or y_col not in live_data:
return

self.ax.clear()
self.ax.plot(live_data[x_col], live_data[y_col], marker=".", color="k",
label=f"{y_col} vs {x_col}")
self.ax.set_xlabel(x_col)
self.ax.set_ylabel(y_col)
self.canvas.draw()


Expand Down
Loading
Loading