Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Bug]: possible race condition between TextBoxes with toolmanager and QtAgg #29687

Copy link
Copy link
Open
@derrickturk

Description

@derrickturk
Issue body actions

Bug summary

Under the QtAgg backend, when creating multiple TextBox widgets with toolmanager in use, clicking in one TextBox and then directly in another sometimes results in ValueError('already locked') from TextBox.begin_typing.

Code for reproduction

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))

Actual outcome

Traceback (most recent call last):
  File "/redacted/.venv/lib/python3.13/site-packages/matplotlib/cbook.py", line 361, in process
    func(*args, **kwargs)
    ~~~~^^^^^^^^^^^^^^^^^
  File "/redacted/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 1499, in _click
    self.begin_typing()
    ~~~~~~~~~~~~~~~~~^^
  File "/redacted/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 1464, in begin_typing
    toolmanager.keypresslock(self)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/redacted/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 44, in __call__
    raise ValueError('already locked')
ValueError: already locked

Expected outcome

I'd expect to be able to navigate from one text box to the other without this happening.

Additional information

I believe this is related to the changes in #14343. I subclassed TextBox to get a little more information:

import os
import sys

import matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox


matplotlib.use('QTAgg')
matplotlib.rcParams['toolbar'] = 'toolmanager'
print(matplotlib.get_backend())

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))

With this instrumentation, I sometimes see the following trace when clicking between textboxes:

TB2 BEGIN TYPING
TB1 BEGIN TYPING
Traceback (most recent call last):
  File "/home/dwt/work/vso/VSO_GBT2/.venv/lib/python3.13/site-packages/matplotlib/cbook.py", line 361, in process
    func(*args, **kwargs)
    ~~~~^^^^^^^^^^^^^^^^^
  File "/home/dwt/work/vso/VSO_GBT2/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 1499, in _click
    self.begin_typing()
    ~~~~~~~~~~~~~~~~~^^
  File "/home/dwt/work/vso/VSO_GBT2/demo.py", line 20, in begin_typing
    super().begin_typing()
    ~~~~~~~~~~~~~~~~~~~~^^
  File "/home/dwt/work/vso/VSO_GBT2/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 1464, in begin_typing
    toolmanager.keypresslock(self)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/home/dwt/work/vso/VSO_GBT2/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 44, in __call__
    raise ValueError('already locked')
ValueError: already locked
TB2 STOP TYPING

Which implies that (perhaps this is related to the QtAgg backend specifically?) the begin_typing method on the "new" textbox is sometimes fired before the stop_typing method on the "old" textbox. begin_typing tries to take the keypresslock, but the other textbox still holds it (it's released in stop_typing).

Operating system

Arch Linux (6.12.7-arch1-1)

Matplotlib Version

3.10.0

Matplotlib Backend

QtAgg

Python version

No response

Jupyter version

No response

Installation

pip

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.