Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: blitting after closing second tkinter embed causes silent crash #23809

Closed
Alex-x90 opened this issue Sep 6, 2022 · 3 comments · Fixed by #25104
Closed

[Bug]: blitting after closing second tkinter embed causes silent crash #23809

Alex-x90 opened this issue Sep 6, 2022 · 3 comments · Fixed by #25104
Labels
Milestone

Comments

@Alex-x90
Copy link

Alex-x90 commented Sep 6, 2022

Bug summary

After opening a tkinter popup and embedding a figure a second time and closing it, blitting the figure again causes a silent complete program crash.

Code for reproduction

from tkinter import *
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

mainWindow = Tk()

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
img = ax.imshow(np.random.rand(28,28), cmap="gray", interpolation="None")
fig.canvas.draw()

canvas = FigureCanvasTkAgg(fig, master=mainWindow)
canvas.draw()
canvas.get_tk_widget().grid(row=1, column=1)

def openPopup():
    popup = Tk()

    popupCanvas = FigureCanvasTkAgg(fig, master=popup)
    popupCanvas.draw()
    popupCanvas.get_tk_widget().grid(row=1, column=1)

    popup.protocol("WM_DELETE_WINDOW", popup.destroy)
    popup.mainloop()

def doBlit():
    img.set_data(np.random.rand(28,28))
    ax.draw_artist(img)
    
    fig.canvas.blit(ax.bbox) # this blit line is the cause of the crash

    fig.canvas.flush_events()

button_1 = Button(mainWindow,command=(openPopup),text="open popup")
button_1.grid(row=1,column=2)
button_2 = Button(mainWindow,command=(doBlit),text="do blitting")
button_2.grid(row=1,column=3)

mainWindow.mainloop()

Actual outcome

Program crashes with no error message.

Expected outcome

Either the program should crash with an error message, or the blit should succeed and the new image should be rendered.

Additional information

I think this is probably related to #16580, but the lack of any error message is definitely different. In the larger program where I'm trying this blitting doesn't actively crash in this way with a separate figure that has 2 scatterplots and a title which are being blitted and embedded in the same manner, but the other figure which is just an image is causing the program to crash. If anyone knows a way to fix this in the meantime I would be very grateful.

Operating system

Windows

Matplotlib Version

3.4.3

Matplotlib Backend

Agg

Python version

3.8.1

Jupyter version

6.4.6

Installation

pip

@anntzer
Copy link
Contributor

anntzer commented Sep 6, 2022

Do either of the fixes I suggested in #16580 help?

@anntzer anntzer added the GUI: tk label Sep 6, 2022
@Alex-x90
Copy link
Author

Alex-x90 commented Sep 7, 2022

The first fix stops the complete crash, but the blitting still fails with the following error message(s).

Traceback (most recent call last):
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\site-packages\
matplotlib\backends\_backend_tk.py", line 117, in blit
    photoimage.tk.call(_blit_tcl_name, argsid)
_tkinter.TclError: invalid command name "pyimage4"

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\tkinter\__init
__.py", line 1883, in __call__
    return self.func(*args)
  File "errorExample.py", line 32, in doBlit
    fig.canvas.blit(ax.bbox)
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\site-packages\
matplotlib\backends\backend_tkagg.py", line 13, in blit
    _backend_tk.blit(self._tkphoto, self.renderer.buffer_rgba(),
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\site-packages\
matplotlib\backends\_backend_tk.py", line 122, in blit
    photoimage.tk.call(_blit_tcl_name, argsid)
_tkinter.TclError
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\site-packages\
matplotlib\backends\_backend_tk.py", line 117, in blit
    photoimage.tk.call(_blit_tcl_name, argsid)
_tkinter.TclError: invalid command name "pyimage4"

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\tkinter\__init
__.py", line 1883, in __call__
    return self.func(*args)
  File "errorExample.py", line 27, in openPopup
    popup.mainloop()
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\tkinter\__init
__.py", line 1420, in mainloop
    self.tk.mainloop(n)
  File "C:\Users\sasha\AppData\Local\Programs\Python\Python38\lib\site-packages\
matplotlib\backends\_backend_tk.py", line 63, in _blit
    photoimage, dataptr, offsets, bboxptr, blank = _blit_args.pop(argsid)
KeyError: '1510834383744'

The second fix still crashes completely silently. The fact that the scatterplot blitting isn't having any issues in my circumstances but blitting the image does have issues seems strange to me. Also something I just noticed, if I open the popup and press the blit button only the popup blits, the main window stays completely static. Is there some way to force the blitting to stay locked onto the main window and not switch to the popup?

@hugochrist1
Copy link

hugochrist1 commented Sep 19, 2022

It crashes because you use the same figure for the two windows. When you close your popup, you need to change the canvas of the figure so it refers to the main window again. Also, try not to use pyplot with tkinter, instead use Figure.

from tkinter import *
import numpy as np
import matplotlib
from matplotlib.figure import Figure
matplotlib.use("Agg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

mainWindow = Tk()

fig = Figure()
ax = fig.add_subplot(1,1,1)
img = ax.imshow(np.random.rand(28,28), cmap="gray", interpolation="None")
fig.canvas.draw()

canvas = FigureCanvasTkAgg(fig, master=mainWindow)
canvas.draw()
canvas.get_tk_widget().grid(row=1, column=1)

def close_popup(popup):
    fig.set_canvas(canvas)
    canvas.draw() # This is necessary, no idea why
    popup.quit()
    popup.destroy()

def openPopup():
    popup = Toplevel()

    popupCanvas = FigureCanvasTkAgg(fig, master=popup)
    popupCanvas.draw()
    popupCanvas.get_tk_widget().grid(row=1, column=1)

    popup.protocol("WM_DELETE_WINDOW", lambda p=popup: close_popup(popup))
    popup.mainloop()

def doBlit():
    img.set_data(np.random.rand(28,28))
    ax.draw_artist(img)
    
    fig.canvas.blit(fig.bbox) # this blit line is the cause of the crash

    fig.canvas.flush_events()

button_1 = Button(mainWindow,command=(openPopup),text="open popup")
button_1.grid(row=1,column=2)
button_2 = Button(mainWindow,command=(doBlit),text="do blitting")
button_2.grid(row=1,column=3)

mainWindow.mainloop()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants