Search code examples
pythonqtpyqtqtableviewqfiledialog

QFileDialog as editor for TableView: how to get result?


I'm using a QFileDialog as the editor for some columns in a QTableView. This basically works (modulo some focus issues, see here):

class DirectorySelectionDelegate(QStyledItemDelegate):    
    def createEditor(self, parent, option, index):
        editor = QFileDialog(parent)
        editor.setFileMode(QFileDialog.Directory)       
        editor.setModal(True)
        return editor    

    def setEditorData(self, editor, index):
        val = index.model().data(index, Qt.DisplayRole)
        fs = val.rsplit(os.path.sep, 1)
        if len(fs) == 2:
            bdir, vdir = fs
        else:
            bdir = "."
            vdir = fs[0]

        editor.setDirectory(bdir)        
        editor.selectFile(vdir)        

    def setModelData(self, editor, model, index):
        model.setData(index, editor.selectedFiles()[0])   

    def updateEditorGeometry(self, editor, option, index):
        r = option.rect
        r.setHeight(600)
        r.setWidth(600)            
        editor.setGeometry(r)

However, when the editor is closed I don't see a way to differentiate between Choose and Cancel (or lost focus), the setEditorData function is called in all cases. I don't see a way to get the result from QFileDialog that I get as editor, all examples I can find use the return value from exec_, which I don't have access to.


Solution

  • In setModelData, it looks like you could check the editor's result before setting the model's data. By default, the result is QDialog.Rejected, and that should only change if the user actually chooses a file:

        def setModelData(self, editor, model, index):
            if editor.result() == QtGui.QDialog.Accepted:
                model.setData(index, editor.selectedFiles()[0])   
    

    UPDATE:

    After some belated testing, it's obvious that no matter how the file-dialog is run (even with exec), its result will never get properly reset in the context of a delegate editor. So a little indirection is needed. According to the docs for QFileDialog.filesSelected, this signal will always and only be sent when the dialog is accepted (even if there are no selected files). So we can use this mechanism to force the correct dialog result, like this:

    class DirectorySelectionDelegate(QtGui.QStyledItemDelegate):
        def createEditor(self, parent, option, index):
            editor = QtGui.QFileDialog(parent)
            editor.filesSelected.connect(
                lambda: editor.setResult(QtGui.QDialog.Accepted))
            ...
    
        def setModelData(self, editor, model, index):
            print(editor.result())
            if editor.result() == QtGui.QDialog.Accepted:
                model.setData(index, editor.selectedFiles()[0])