Skip to content

Commit e60da20

Browse files
committed
FIX: Add update capability to interval/singleshot timer properties
1 parent ed8131b commit e60da20

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

lib/matplotlib/tests/test_backends_interactive.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,29 +626,47 @@ def _impl_test_interactive_timers():
626626
# We only want singleshot if we specify that ourselves, otherwise we want
627627
# a repeating timer
628628
import os
629+
import sys
629630
from unittest.mock import Mock
630631
import matplotlib.pyplot as plt
631632
# increase pause duration on CI to let things spin up
632633
# particularly relevant for gtk3cairo
633634
pause_time = 2 if os.getenv("CI") else 0.5
635+
expected_100ms_calls = int(pause_time / 0.1)
634636
fig = plt.figure()
635637
plt.pause(pause_time)
636638
timer = fig.canvas.new_timer(0.1)
637639
mock = Mock()
638640
timer.add_callback(mock)
639641
timer.start()
640642
plt.pause(pause_time)
641-
timer.stop()
642-
assert mock.call_count > 1
643+
# NOTE: The timer is as fast as possible, but this is different between backends
644+
# so we can't assert on the exact number but it should be faster than 100ms
645+
assert mock.call_count > expected_100ms_calls, \
646+
f"Expected more than {expected_100ms_calls} calls, got {mock.call_count}"
647+
648+
# Test updating the interval updates a running timer
649+
timer.interval = 100
650+
mock.call_count = 0
651+
plt.pause(pause_time)
652+
# GTK4 on macos runners produces about 3x as many calls as expected
653+
# It works locally and on Linux though, so only skip when running on CI
654+
if not (os.getenv("CI")
655+
and "gtk4" in os.getenv("MPLBACKEND")
656+
and sys.platform == "darwin"):
657+
# Could be off due to when the timers actually get fired (especially on CI)
658+
assert 1 < mock.call_count <= expected_100ms_calls + 1, \
659+
f"Expected less than {expected_100ms_calls + 1} calls, " \
660+
"got {mock.call_count}"
643661

644662
# Now turn it into a single shot timer and verify only one gets triggered
645663
mock.call_count = 0
646664
timer.single_shot = True
647-
timer.start()
648665
plt.pause(pause_time)
649666
assert mock.call_count == 1
650667

651-
# Make sure we can start the timer a second time
668+
# Make sure we can start the timer after stopping
669+
timer.stop()
652670
timer.start()
653671
plt.pause(pause_time)
654672
assert mock.call_count == 2

src/_macosx.m

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,6 +1789,18 @@ - (void)flagsChanged:(NSEvent *)event
17891789
Py_RETURN_NONE;
17901790
}
17911791

1792+
static PyObject*
1793+
Timer__timer_update(Timer* self)
1794+
{
1795+
// stop and invalidate a timer if it is already running and then create a new one
1796+
// where the start() method retrieves the updated interval internally
1797+
if (self->timer) {
1798+
Timer__timer_stop_impl(self);
1799+
gil_call_method((PyObject*)self, "_timer_start");
1800+
}
1801+
Py_RETURN_NONE;
1802+
}
1803+
17921804
static void
17931805
Timer_dealloc(Timer* self)
17941806
{
@@ -1815,6 +1827,12 @@ - (void)flagsChanged:(NSEvent *)event
18151827
{"_timer_stop",
18161828
(PyCFunction)Timer__timer_stop,
18171829
METH_NOARGS},
1830+
{"_timer_set_interval",
1831+
(PyCFunction)Timer__timer_update,
1832+
METH_NOARGS},
1833+
{"_timer_set_single_shot",
1834+
(PyCFunction)Timer__timer_update,
1835+
METH_NOARGS},
18181836
{} // sentinel
18191837
},
18201838
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy