Opening (Py)Qt save file dialogs from a Jupyter Notebook

I don’t know if this is the right place for this question, but the behavior only happens in Jupyter notebooks so I thought I’d give it a try.

I’m writing a function that creates an interactive matplotlib figure inside a notebook for visualizing a stack of images. I have a couple sliders to select the image frame, to adjust contrast and brightness, and a checkbox to add a scalebar. Now I want to add a “save” button to this figure which opens a file dialog to allow the user to browse to a location to save the current view. I’m on Mac (OSX Mojave 10.14.6) and Tkinter gives me terrible problems (i.e. complete system crashes) so I was looking at PyQt’s QFileDialog for the job. However, it seems like Jupyter doesn’t like working with Qt. When I try to open the filedialog directly from a cell in the notebook, the kernel always dies. So instead I’ve written a rather convoluted script:

from PyQt5.QtWidgets import QApplication, QFileDialog, QWidget


class SaveFileDialog(QWidget):

    def __init__(self, filters="All Files (*)"):
        super().__init__()
        self.title = "Save file"
        self.filters = filters
        self.filename = self.saveFileDialog()

    def saveFileDialog(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        filename, _ = QFileDialog.getSaveFileName(caption=self.title,
                                                  filter=self.filters,
                                                  options=options)
        return filename


def get_fname(directory='./', filters="All files (*)"):
    app = QApplication([directory])
    fd = SaveFileDialog(filters=filters)
    return fd.filename


if __name__ == "__main__":
    s = get_fname()
    print(s)

When I run this from the command line, either with python script.py or by running get_fname in IPython, I get the expected behavior: a filename is printed in the terminal and I can interact with the dialog. The reason I wrap it in this widget is because if I directly call QFileDialog.getSaveFileName it opens the dialog also, but it doesn’t respond to keyboard input and thus I can’t enter a filename.

Anyway, using this exact code and calling get_fname() in a notebook does not work. It opens up the file dialog behind the browser window. It responds to mouse clicks, but not to keyboard input. Keyboard input remains directed a the notebook.

Does anyone know what the reason for this is and how to fix it? I’m open to any solution (not involving Tkinter) to opening a file dialog from a jupyter notebook and letting the user enter a save file location. Thanks.