Skip to content

Instantly share code, notes, and snippets.

@derrickturk
Last active February 27, 2025 17:33
Show Gist options
  • Save derrickturk/aaa6c52d96b1719ea22ea25c644434a8 to your computer and use it in GitHub Desktop.
Save derrickturk/aaa6c52d96b1719ea22ea25c644434a8 to your computer and use it in GitHub Desktop.
race condition in matplotlib.widgets.TextBox with QTAgg back end and toolmanager in use
import os
import sys
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
matplotlib.use('QTAgg')
matplotlib.rcParams['toolbar'] = 'toolmanager'
class SnitchBox(TextBox):
def __init__(self, name, *rest) -> None:
self.name = name
super().__init__(*rest)
def begin_typing(self) -> None:
print(f'{self.name} BEGIN TYPING')
super().begin_typing()
def stop_typing(self) -> None:
print(f'{self.name} STOP TYPING')
super().stop_typing()
def main(argv: list[str]) -> int:
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
xs = list(range(25))
ys = [x ** 3 for x in xs]
plt.plot(xs, ys)
tb1 = SnitchBox('TB1', plt.axes([0.15, 0.05, 0.3, 0.075]), 'TB1')
tb2 = SnitchBox('TB2', plt.axes([0.55, 0.05, 0.3, 0.075]), 'TB2')
plt.show()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
import os
import sys
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
matplotlib.use('QTAgg')
matplotlib.rcParams['toolbar'] = 'toolmanager'
def main(argv: list[str]) -> int:
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
xs = list(range(25))
ys = [x ** 3 for x in xs]
plt.plot(xs, ys)
tb1 = TextBox(plt.axes([0.15, 0.05, 0.3, 0.075]), 'TB1')
tb2 = TextBox(plt.axes([0.55, 0.05, 0.3, 0.075]), 'TB2')
plt.show()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
import os
import sys
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.axes import Axes
from matplotlib.widgets import TextBox
matplotlib.use('QTAgg')
matplotlib.rcParams['toolbar'] = 'toolmanager'
class SnitchBox(TextBox):
def __init__(self, name, *rest) -> None:
self.name = name
super().__init__(*rest)
def begin_typing(self) -> None:
print(f'{self.name} BEGIN TYPING')
super().begin_typing()
def stop_typing(self) -> None:
print(f'{self.name} STOP TYPING')
super().stop_typing()
def unfuck_keypresslock(axes: Axes) -> None:
fig = axes.get_figure(root=True)
if fig is None:
return
toolmanager = getattr(fig.canvas.manager, "toolmanager", None)
if toolmanager is None:
return
class UnfuckedLock:
def __init__(self) -> None:
self._owners: set[int] = set()
def __call__(self, owner: object) -> None:
self._owners.add(id(owner))
def release(self, owner: object) -> None:
self._owners.remove(id(owner))
def locked(self) -> bool:
return bool(self._owners)
toolmanager.keypresslock = UnfuckedLock()
def main(argv: list[str]) -> int:
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
xs = list(range(25))
ys = [x ** 3 for x in xs]
plt.plot(xs, ys)
unfuck_keypresslock(ax)
tb1 = SnitchBox('TB1', plt.axes((0.15, 0.05, 0.3, 0.075)), 'TB1')
tb2 = SnitchBox('TB2', plt.axes((0.55, 0.05, 0.3, 0.075)), 'TB2')
plt.show()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment