Skip to content

Commit 95e8357

Browse files
K6517B, T_Sensing, Removed blitting tried making this simpler
1 parent 47f46d9 commit 95e8357

1 file changed

Lines changed: 72 additions & 67 deletions

File tree

pica/keithley/k6517b/High_Resistance/RT_K6517B_L350_T_Sensing_GUI.py

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def __init__(self, root):
256256
self.logo_image = None # Attribute to hold the logo image reference
257257
self.data_queue = queue.Queue()
258258
self.measurement_thread = None
259-
self.plot_backgrounds = None # For blitting
259+
self._plot_dirty = False
260260

261261
self.setup_styles()
262262
self.create_widgets()
@@ -694,7 +694,7 @@ def create_graph_frame(self, parent):
694694
self.ax_sub2 = self.figure.add_subplot(
695695
gs[1, 1]) # Temp vs Time has its own X-axis
696696
self.line_main, = self.ax_main.plot(
697-
[], [], color=self.CLR_ACCENT_RED, marker='o', markersize=3, linestyle='-', animated=True)
697+
[], [], color=self.CLR_ACCENT_RED, marker='o', markersize=3, linestyle='-')
698698
self.ax_main.set_title("Resistance vs. Temperature", fontweight='bold')
699699
self.ax_main.set_ylabel("Resistance (Ω)")
700700
if self.log_scale_var.get():
@@ -703,35 +703,22 @@ def create_graph_frame(self, parent):
703703
self.ax_main.set_yscale('linear')
704704
self.ax_main.grid(True, which="both", linestyle='--', alpha=0.6)
705705
self.line_sub1, = self.ax_sub1.plot(
706-
[], [], color=self.CLR_ACCENT_GOLD, marker='.', markersize=3, linestyle='-', animated=True)
706+
[], [], color=self.CLR_ACCENT_GOLD, marker='.', markersize=3, linestyle='-')
707707
self.ax_sub1.set_xlabel("Temperature (K)")
708708
self.ax_sub1.set_ylabel("Current (A)")
709709
self.ax_sub1.grid(True, linestyle='--', alpha=0.6)
710710
self.line_sub2, = self.ax_sub2.plot(
711-
[], [], color=self.CLR_ACCENT_GREEN, marker='.', markersize=3, linestyle='-', animated=True)
711+
[], [], color=self.CLR_ACCENT_GREEN, marker='.', markersize=3, linestyle='-')
712712
self.ax_sub2.set_xlabel("Time (s)")
713713
self.ax_sub2.set_ylabel("Temperature (K)")
714714
self.ax_sub2.grid(True, linestyle='--', alpha=0.6)
715715
self.figure.tight_layout(pad=3.0)
716716
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
717717

718718
def _update_y_scale(self):
719-
if self.log_scale_var.get():
720-
self.ax_main.set_yscale('log')
721-
else:
722-
self.ax_main.set_yscale('linear')
723-
# If the measurement is running, we need to redraw and recapture the
724-
# background
725-
if self.is_running and self.plot_backgrounds:
726-
self.canvas.draw()
727-
self.plot_backgrounds = [
728-
self.canvas.copy_from_bbox(
729-
ax.bbox) for ax in [
730-
self.ax_main,
731-
self.ax_sub1,
732-
self.ax_sub2]]
733-
else:
734-
self.canvas.draw_idle()
719+
self.ax_main.set_yscale('log' if self.log_scale_var.get() else 'linear')
720+
self._plot_dirty = True # force a rescale on next refresh
721+
self.canvas.draw_idle()
735722

736723
def log(self, message):
737724
timestamp = datetime.now().strftime("%H:%M:%S")
@@ -789,17 +776,7 @@ def start_measurement(self):
789776
f"R-T Curve: {params['sample_name']}",
790777
fontweight='bold')
791778

792-
# --- MODIFIED: Setup for blitting ---
793-
self.canvas.draw() # Full draw to prepare background
794-
self.plot_backgrounds = [
795-
self.canvas.copy_from_bbox(
796-
ax.bbox) for ax in [
797-
self.ax_main,
798-
self.ax_sub1,
799-
self.ax_sub2]]
800-
for line in [self.line_main, self.line_sub1, self.line_sub2]:
801-
line.set_animated(True)
802-
self.log("Blitting enabled for fast graph updates.")
779+
self.canvas.draw_idle()
803780

804781
self.log("Starting passive data logging...")
805782
self.start_time = time.time()
@@ -808,6 +785,7 @@ def start_measurement(self):
808785
target=self._measurement_worker, daemon=True)
809786
self.measurement_thread.start()
810787
self.root.after(100, self._process_data_queue)
788+
self.root.after(250, self._refresh_plot)
811789

812790
except Exception as e:
813791
self.log(f"ERROR during startup: {traceback.format_exc()}")
@@ -819,10 +797,6 @@ def stop_measurement(self, from_user=True):
819797
if self.is_running:
820798
self.is_running = False
821799
self.log("Measurement stopped by user.")
822-
# --- MODIFIED: Disable blitting on stop ---
823-
for line in [self.line_main, self.line_sub1, self.line_sub2]:
824-
line.set_animated(False)
825-
self.plot_backgrounds = None
826800
self.canvas.draw_idle()
827801
self.start_button.config(state='normal')
828802
self.stop_button.config(state='disabled')
@@ -872,45 +846,76 @@ def _process_data_queue(self):
872846
self.data_storage['temperature'].append(temp)
873847
self.data_storage['current'].append(cur)
874848
self.data_storage['resistance'].append(res)
875-
876-
# --- MODIFIED: Use blitting for fast graph updates ---
877-
if self.plot_backgrounds:
878-
# Restore the clean backgrounds
879-
for bg in self.plot_backgrounds:
880-
self.canvas.restore_region(bg)
881-
882-
# Update data for all lines
883-
self.line_main.set_data(
884-
self.data_storage['temperature'],
885-
self.data_storage['resistance'])
886-
self.line_sub1.set_data(
887-
self.data_storage['temperature'],
888-
self.data_storage['current'])
889-
self.line_sub2.set_data(
890-
self.data_storage['time'],
891-
self.data_storage['temperature'])
892-
893-
# Redraw only the artists and blit the changes
894-
for ax, line in zip([self.ax_main, self.ax_sub1, self.ax_sub2], [
895-
self.line_main, self.line_sub1, self.line_sub2]):
896-
ax.relim()
897-
ax.autoscale_view()
898-
ax.draw_artist(line)
899-
900-
self.canvas.blit(self.figure.bbox)
901-
else: # Fallback to full redraw if blitting isn't ready
902-
for ax in [self.ax_main, self.ax_sub1, self.ax_sub2]:
903-
ax.relim()
904-
ax.autoscale_view()
905-
self.figure.tight_layout(pad=3.0)
906-
self.canvas.draw_idle()
849+
# Mark that the plot needs a refresh; actual redraw is
850+
# decoupled and throttled (see _refresh_plot).
851+
self._plot_dirty = True
907852

908853
except queue.Empty:
909854
pass
910855

911856
if self.is_running:
912857
self.root.after(200, self._process_data_queue)
913858

859+
def _refresh_plot(self):
860+
"""Redraws the plots at a fixed cadence, independent of data rate.
861+
862+
A normal (non-blitted) draw is used so that the axes — ticks,
863+
limits, gridlines and scale — always stay in sync with the data.
864+
"""
865+
if self._plot_dirty:
866+
self._plot_dirty = False
867+
868+
temps = self.data_storage['temperature']
869+
res = self.data_storage['resistance']
870+
cur = self.data_storage['current']
871+
t = self.data_storage['time']
872+
873+
self.line_main.set_data(temps, res)
874+
self.line_sub1.set_data(temps, cur)
875+
self.line_sub2.set_data(t, temps)
876+
877+
# Recompute and apply limits on every axis.
878+
self._autoscale_axis(self.ax_main, x=temps, y=res,
879+
log_y=self.log_scale_var.get())
880+
self._autoscale_axis(self.ax_sub1, x=temps, y=cur)
881+
self._autoscale_axis(self.ax_sub2, x=t, y=temps)
882+
883+
# Full redraw keeps ticks/labels/gridlines correct and is
884+
# resize-proof. draw_idle() coalesces redraws efficiently.
885+
self.canvas.draw_idle()
886+
887+
if self.is_running:
888+
self.root.after(250, self._refresh_plot)
889+
890+
def _autoscale_axis(self, ax, x, y, log_y=False, margin=0.05):
891+
"""Rescale an axis, ignoring non-finite and (for log) non-positive
892+
values so the axis never collapses or freezes."""
893+
import math
894+
895+
xs = [v for v in x if v is not None and math.isfinite(v)]
896+
if log_y:
897+
ys = [v for v in y if v is not None and math.isfinite(v) and v > 0]
898+
else:
899+
ys = [v for v in y if v is not None and math.isfinite(v)]
900+
901+
if not xs or not ys:
902+
return
903+
904+
xmin, xmax = min(xs), max(xs)
905+
ymin, ymax = min(ys), max(ys)
906+
907+
# X padding (linear)
908+
xpad = (xmax - xmin) * margin or 0.5
909+
ax.set_xlim(xmin - xpad, xmax + xpad)
910+
911+
# Y padding
912+
if log_y:
913+
# pad multiplicatively in log space
914+
ax.set_ylim(ymin / (1 + margin), ymax * (1 + margin))
915+
else:
916+
ypad = (ymax - ymin) * margin or abs(ymax) * margin or 1e-12
917+
ax.set_ylim(ymin - ypad, ymax + ypad)
918+
914919
def _scan_for_visa_instruments(self):
915920
if not pyvisa:
916921
self.log("ERROR: PyVISA is not installed.")

0 commit comments

Comments
 (0)