From b37130cfd202490e789752f6e28763e9e44b0b0a Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Tue, 16 Dec 2025 14:11:41 +0000
Subject: [PATCH 1/4] Add GHA test
---
.github/workflows/build.yml | 40 +++++++++++++++++++++++++++++++++++++
.gitignore | 1 +
2 files changed, 41 insertions(+)
create mode 100644 .github/workflows/build.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..bbe2932
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,40 @@
+name: Test viewer
+
+on:
+ push:
+ branches: [ main, development, test* ]
+ pull_request:
+ branches: [ main, development, test* ]
+
+jobs:
+ build:
+
+ runs-on: ${{ matrix.runs-on }}
+ strategy:
+ fail-fast: false
+ matrix:
+ runs-on: [ubuntu-latest, macos-latest ]
+ python-version: [ "3.10", "3.12" ]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+
+ - name: Install Python dependencies
+ run: |
+
+ pip install numpy matplotlib ruff
+
+ - name: Test WCONViewer
+ run: |
+ ./test_all.sh
+
+
+ - name: Print info on files
+ run: |
+ ls -alt
diff --git a/.gitignore b/.gitignore
index 225fc6f..6cd32ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/__pycache__
+/.DS_Store
From d1a2a4afaaf01cc3ab127d23d4d04b7c458f3c2a Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Tue, 16 Dec 2025 14:59:30 +0000
Subject: [PATCH 2/4] Install ffmpeg also
---
.github/workflows/build.yml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bbe2932..f9fc6dd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,9 +25,10 @@ jobs:
python-version: ${{ matrix.python-version }}
- - name: Install Python dependencies
+ - name: Install dependencies
run: |
-
+ sudo apt-get update
+ sudo apt-get install ffmpeg -y
pip install numpy matplotlib ruff
- name: Test WCONViewer
From 26712f7288e269b8260998ca33e937497727ba4b Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Tue, 16 Dec 2025 15:11:05 +0000
Subject: [PATCH 3/4] Just test on ubuntu for now
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f9fc6dd..68107df 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- runs-on: [ubuntu-latest, macos-latest ]
+ runs-on: [ubuntu-latest]
python-version: [ "3.10", "3.12" ]
steps:
From 46626e9dfdd46a4b755f4b3c5f7fae6f32cf185d Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Tue, 16 Dec 2025 15:11:59 +0000
Subject: [PATCH 4/4] Better display of time
---
Player.py | 23 +++++++++++++++++++++++
WormView.py | 25 +++++++++++++++++++------
2 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/Player.py b/Player.py
index 5dc9a1c..94ac666 100644
--- a/Player.py
+++ b/Player.py
@@ -19,6 +19,8 @@ def __init__(
mini=0,
maxi=100,
pos=(0.125, 0.92),
+ times=None,
+ t_units="",
**kwargs,
):
self.i = 0
@@ -39,6 +41,8 @@ def __init__(
save_count=save_count,
**kwargs,
)
+ self.times = times
+ self.t_units = t_units
def play(self):
while self.runs:
@@ -82,6 +86,7 @@ def onestep(self):
self.i -= 1
self.func(self.i)
self.slider.set_val(self.i)
+
self.fig.canvas.draw_idle()
def setup(self, pos):
@@ -105,14 +110,32 @@ def setup(self, pos):
self.slider = matplotlib.widgets.Slider(
sliderax, "", self.min, self.max, valinit=self.i
)
+ self.slider.valtext.set_visible(False)
self.slider.on_changed(self.set_pos)
+ self.slider.label.set_horizontalalignment("left")
+ label_position = self.slider.label.get_position() # Get the current position
+ self.slider.label.set_position(
+ (label_position[0] + 1.1, label_position[1])
+ ) # Adjust x-coordinate
+
+ # self.text_box = matplotlib.widgets.TextBox(textax, label="", initial="0 ms")
def set_pos(self, i):
self.i = int(self.slider.val)
self.func(self.i)
+ self.set_time_text(self.i)
+
+ def set_time_text(self, i):
+ time_text = str(i)
+ if self.times is not None:
+ time_text = "%s%s" % (self.times[i], self.t_units)
+
+ # self.text_box.set_val(time_text)
+ self.slider.label.set_text(time_text)
def update(self, i):
self.slider.set_val(i)
+ self.set_time_text(i)
if __name__ == "__main__":
diff --git a/WormView.py b/WormView.py
index d85e7f1..1263564 100644
--- a/WormView.py
+++ b/WormView.py
@@ -127,12 +127,22 @@ def main():
ax.set_ylim([-1.5, 1.5])
- t = np.array(wcon["data"][0]["t"])
+ t_units = ""
+ x_units = ""
+ y_units = ""
+
+ if "units" in wcon:
+ t_units = wcon["units"].get("t")
+ x_units = wcon["units"].get("x")
+ y_units = wcon["units"].get("y")
+ print(f"Time units: {t_units}, x units: {x_units}, y units: {y_units}")
+
+ times = np.array(wcon["data"][0]["t"])
x = np.array(wcon["data"][0]["x"]).T
y = np.array(wcon["data"][0]["y"]).T
print(
- f"Range of time: {t[0]}->{t[0]}; x range: {x.max()}->{x.min()}; y range: {y.max()}->{y.min()}"
+ f"Range of time: {times[0]}{t_units}->{times[-1]}{t_units}; x range: {x.max()}{x_units}->{x.min()}{x_units}; y range: {y.max()}{y_units}->{y.min()}{y_units}"
)
factor = 0.05
if abs(x.max() - x.min()) > abs(y.max() - y.min()):
@@ -146,7 +156,7 @@ def main():
mid = (x.max() + x.min()) / 2
ax.set_xlim([mid - side * (0.5 + factor), mid + side * (0.5 + factor)])
- num_steps = t.size
+ num_steps = times.size
if "px" in wcon["data"][0] and "py" in wcon["data"][0]:
if args.ignore_wcon_perimeter:
@@ -170,13 +180,14 @@ def main():
def update(ti):
global midline_plot, perimeter_plot
f = ti / num_steps
+ t = times[ti]
color = "#%02x%02x00" % (int(0xFF * (f)), int(0xFF * (1 - f) * 0.8))
- print("Time step: %s, fract: %f, color: %s" % (ti, f, color))
+ print("Time %s%s, step: %s, fract: %f, color: %s" % (t, t_units, ti, f, color))
if midline_plot is None:
(midline_plot,) = ax.plot(
- x[:, ti], y[:, ti], color="g", label="t=%sms" % t[ti], linewidth=0.5
+ x[:, ti], y[:, ti], color="g", label="t=%sms" % times[ti], linewidth=0.5
)
else:
midline_plot.set_data(x[:, ti], y[:, ti])
@@ -189,7 +200,9 @@ def update(ti):
else:
perimeter_plot.set_data(px[:, ti], py[:, ti])
- anim = Player(fig, update, maxi=num_steps - 1)
+ anim = Player(
+ fig, update, maxi=num_steps - 1, times=[t for t in times], t_units=t_units
+ )
# TODO WormViewCSV and WormViewWCON - should WormViewCSV just be the original WormView? That's what it initially did.
# TODO Could take out Player and WormViewWCON into separate repo - Taking out Player could be ugly. It is quite coupled with WormView due to the update function.