Skip to content

add single wf visualiser - #85

Open
MattZur wants to merge 13 commits into
nu-ZOO:mainfrom
MattZur:Add-single-wf-visualiser
Open

add single wf visualiser#85
MattZur wants to merge 13 commits into
nu-ZOO:mainfrom
MattZur:Add-single-wf-visualiser

Conversation

@MattZur

@MattZur MattZur commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Adds to packs the choice of vis. This allows the mule hdf5's to be visualised one waveform at the time.

Currently, the only option is waveform as in single waveform visualiser. Other options such as average, smoothened single waveform, and FFT will be added later on.

This initiates a GUI with a scrollable element that allows for the waveforms in the mule hdf5 to be visualised one by one.

@MattZur
MattZur requested review from a team and jwaiton May 11, 2026 14:23

@jwaiton jwaiton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

First comments from a user perspective before I dive into the code a bit more closely:

  • The example config (packs/config/vis_wd1_1channel.conf) should point by default at one of the data files in packs/tests/data/.
  • Due your sampling rate, using plt.plot(..., marker = 'o') makes waveforms with larger sampling size look like so:
Image

So changing it to marker = 'o-' would resolve this

  • The visualiser works on 'waveform number', but for files with multiple channels (WD2), how does this correlate? The visualiser should relate channels and events to the relevant waveform for this case. For one-channel cases, it can just say 'channel 0' :)

@MattZur

MattZur commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

For dynamically using mule_dir in the config so that the example config can be run from anywhere, I think I would need to change io.read_config_file() as configparser does not support this natively. The way I've left it so far it has to be run from mule/${any_subfolder}.

@jwaiton jwaiton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good and works as intended! A couple of things I'd like changed before it can be approved.

  • General documentation of the usage of tkinter could be improved. It's not been used anywhere else in MULE, so it would be nice if there is some explanation of how it's working (filling and padding seems relatively abstract until you read the documentation).
  • Minor style changes.

Comment on lines +37 to +38
ax.plot(time, single_wf,
marker='o-', markerfacecolor='None', linestyle='None', markersize=1)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
ax.plot(time, single_wf,
marker='o-', markerfacecolor='None', linestyle='None', markersize=1)
ax.plot(time, single_wf,
marker='o', markerfacecolor='None', linestyle='solid', markersize=1)

The marker style o- doesn't seem to work for me, this resolves it and adds a solid line to allow for a better visualisation of more discrete points in time.

Before:
Image

After:
Image

Comment thread packs/vis/vis.py
case other:
raise RuntimeError(f"process {other} not currently implemented.")
except KeyError as e:
print(f"\nError in the configuration file, incorrect or missing argument: {e} \n")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these cases (RunTimeError & KeyError) covered by a test?

I wrote some general ones for this sort of behaviour, but please double check :)

Comment thread packs/vis/vis.py
Comment on lines +29 to +30
traceback.print_exc()
sys.exit(2) No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can do something nicer with the handing, as they're duplicated across all differing packs. Might be an interesting job for @Tedsmith100 👀

Comment thread packs/vis/vis.py
Comment on lines +5 to +7
from packs.core.io import read_config_file
from packs.core.core_utils import check_test
from packs.vis.visualise_utils import visualise_waveform

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Super nitpicky, but try and align imports :)

Comment on lines +1 to +10
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk

from packs.core.io import load_evt_info, load_rwf_info
from packs.proc.calibration_utils import subtract_baseline, collect_sidebands

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Likewise try to align imports.

I find the inclusion of tk as shorthand for tkinter and the separate import of ttk a bit messy. I'm unsure of a nicer method right now other than using tk.ttk.___ every time you want to use that part of the library, which I think is also a bit ugly. Up to you on this front.

Comment on lines +82 to +85

# controls frame
ctrl = ttk.Frame(root, padding=8)
ctrl.pack(fill=tk.X)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Likewise, it would be worth commenting here on what this is doing, its taking control of the root frame and adding padding below it for a controller section.

Comment on lines +95 to +96
entry.bind("<Return>", on_entry)
entry.bind("<FocusOut>", on_entry)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Likewise, I'd explain what the controls do here. Allows for adjustment of waveform via the editable number in the GUI

Comment on lines +46 to +53
try:
val = int(entry_var.get())
val = max(0, min(val, max_wf))
entry_var.set(val)
slider_var.set(val)
plot_waveform(val)
except ValueError:
pass

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pretty nice bit of code, I'd try and write a test that forces the ValueError, and checks that val does get set to 0 and the max_wf value if you give it some weird inputs.

For example, currently inputting strings doesn't do anything which isn't bad behaviour at all, but you may want to sanitise the inputs so people can't do anything malicious/stupid.

pass

def on_slider(value : str | None = None):
"""Command for ttk.Scale. Sync the entry box to the slider position and redraw the selected waveform."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

likewise, make the comment a match the formatting :)

ttk.Label(ctrl, text=f"(0 – {max_wf})").pack(side=tk.LEFT)

# draw initial waveform
plot_waveform(0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should probably be a try/except here. I'd create a test for an incorrect file input (you want a specific error to be returned), or a h5 file with the correct path/group/node but the input doesn't work as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants