18

When you perform the same analysis in a notebook on different data files, may be handy to graphically select a data file.

In my python scripts I usually implement a QT dialog that returns the file-name of the selected file:

from PySide import QtCore, QtGui

def gui_fname(dir=None):
    """Select a file via a dialog and return the file name.
    """
    if dir is None: dir ='./'
    fname = QtGui.QFileDialog.getOpenFileName(None, "Select data file...", 
            dir, filter="All files (*);; SM Files (*.sm)")
    return fname[0]

However, running this function from an notebook

full_fname = gui_fname()

causes the kernel to die (and restart):

Interestingly, puttying this 3 command in 3 separate cells works

%matplotlib qt
full_fname = gui_fname()
%matplotlib inline

but when I put those commands in one single cell the kernel dies again.

This prevents to create a function like gui_fname_ipynb() that transparently allows selecting a file with a GUI.

For convenience, I created a notebook illustrating the problem:

Any suggestion on how to execute a dialog for file selection from within an IPython Notebook?

user2304916
  • 7,882
  • 5
  • 39
  • 53

3 Answers3

13

Using Anaconda 5.0.0 on windows (Python 3.6.2, IPython 6.1.0), the following two options are both working for me.

OPTION 1: Entirely in a Jupyter notebook:

CELL 1:

%gui qt

from PyQt5.QtWidgets import QFileDialog

def gui_fname(dir=None):
    """Select a file via a dialog and return the file name."""
    if dir is None: dir ='./'
    fname = QFileDialog.getOpenFileName(None, "Select data file...", 
                dir, filter="All files (*);; SM Files (*.sm)")
    return fname[0]

CELL 2:

gui_fname()

This is working for me but it seems a bit...fragile. If I combine these two things into the same cell, it crashes. Or if I omit the %gui qt, it crashes. If I "restart kernel and run all cells", it doesn't work. So I kinda like this other option...

MORE RELIABLE OPTION: Separate script that opens dialog box in a new process

(Based on mkrog code here.)

PUT THE FOLLOWING IN A SEPARATE PYTHON SCRIPT CALLED blah.py:

from sys import executable, argv
from subprocess import check_output
from PyQt5.QtWidgets import QFileDialog, QApplication

def gui_fname(directory='./'):
    """Open a file dialog, starting in the given directory, and return
    the chosen filename"""
    # run this exact file in a separate process, and grab the result
    file = check_output([executable, __file__, directory])
    return file.strip()

if __name__ == "__main__":
    directory = argv[1]
    app = QApplication([directory])
    fname = QFileDialog.getOpenFileName(None, "Select a file...", 
            directory, filter="All files (*)")
    print(fname[0])

...AND IN YOUR JUPYTER NOTEBOOK

import blah
blah.gui_fname()
Steve Byrnes
  • 2,210
  • 1
  • 20
  • 25
  • This worked for me on Windows 10 with Jupyter (Classic) Notebook v6.1.5, but it is kind of clunky, since I want the notebook to be self-contained. So I use a iPython Notebook magic command "%%writefile <<< code above>>>" to write out the Python input cell source code above to a local file, and then import the module and run it in the next cell. The Qt File Chooser opens as a "pop-behind" rather than a pop-up dialog, which is a bit of a hassle. However, it works. Thank you. – Rich Lysakowski PhD Mar 25 '21 at 06:36
4

I have a universal code where it does its job without any problem. Here is my sugestion:

try:
    from tkinter import Tk
    from tkFileDialog import askopenfilenames
except:
    from tkinter import Tk
    from tkinter import filedialog

Tk().withdraw() # we don't want a full GUI, so keep the root window from appearing
filenames = filedialog.askopenfilenames() # show an "Open" dialog box and return the path to the selected file

print (filenames)

hope it can be useful

  • Is it correct that all of these solutions only work if the jupyter lab instance is running on the same host as the browser? – Robert Lugg Mar 06 '20 at 01:23
  • This does not work with Jupyter Classic Notebook v6.1.5 or Spyder v4.2.3. It hangs the iPythonNB kernel and requires restarting the server kernel. I tried using it in Spyder, and it opens the Tkinter dialog, but only as a "pop-behind", and then after selecting a file, the filenames are never actually returned to the calling program. This gets my downvote in 2021. – Rich Lysakowski PhD Mar 25 '21 at 04:31
2

This behaviour was a bug in IPython:

https://github.com/ipython/ipython/issues/4997

that was fixed here:

https://github.com/ipython/ipython/pull/5077

The function to open a gui dialog should work on current master and on the oncoming 2.0 release.

To date, the last 1.x version (1.2.1) does not include a backport of the fix.

EDIT: The example code still crashes IPython 2.x, see this issue.

user2304916
  • 7,882
  • 5
  • 39
  • 53