Return value from button click

I struggled with returning a value from function which is invoked when I click button in PyQt. That's how I'd like to put a value to the variable:

file_path = self.Button_open.clicked.connect(self.OpenTextFile)

Whole function looks like this:

def OpenTextFile(self):
    dialog = QtGui.QFileDialog()
    dialog.setWindowTitle("Choose a file to open")
    dialog.setFileMode(QtGui.QFileDialog.ExistingFile)
    dialog.setNameFilter("Text (*.txt);; All files (*.*)")
    dialog.setViewMode(QtGui.QFileDialog.Detail)

    filename = QtCore.QStringList()

    if(dialog.exec_()):
        file_name = dialog.selectedFiles()
    plain_text = open(file_name[0]).read()
    self.Editor.setPlainText(plain_text)
    return str(file_name[0])

Now, when I want to pass the file_path to the other function, python interpreter says

self.Button_save.clicked.connect(self.SaveTextFile(file_path)) TypeError: connect() slot argument should be a callable or a signal, not 'NoneType'

Any thoughts how to make it work ?


Solution 1:

Store the file_path in a class level variable and update that value in your button click method.

self.file_path = None
self.Button_open.clicked.connect(self.OpenTextFile)

And then,

def OpenTextFile(self):
    dialog = QtGui.QFileDialog()
    dialog.setWindowTitle("Choose a file to open")
    dialog.setFileMode(QtGui.QFileDialog.ExistingFile)
    dialog.setNameFilter("Text (*.txt);; All files (*.*)")
    dialog.setViewMode(QtGui.QFileDialog.Detail)

    filename = QtCore.QStringList()

    if(dialog.exec_()):
        file_name = dialog.selectedFiles()
    plain_text = open(file_name[0]).read()
    self.Editor.setPlainText(plain_text)
    self.file_path = str(file_name[0])

Also your

self.Button_save.clicked.connect(self.SaveTextFile(file_path))

should be

self.Button_save.clicked.connect(self.SaveTextFile)

and in your save click method

def SaveTextFile(self):
    save(self.file_path)     # Your code to save file

Solution 2:

Your code for getting the file path won't work. The connect method does not call the connected function and will not assign its return value. Typically we might expect the connect to be done when the GUI is created, whereas the file_path variable should be assigned later when the open method is called. You don't provide the code which spans the scope from the open method to the close method, but a possible solution would be to use a class level variable as suggested by Muhammad Tahir Butt.

However, the error you quote comes from this line of code:

self.Button_save.clicked.connect(self.SaveTextFile(file_path))

The problem here is that you are connecting the button click not to the function but to the result of calling the function. Python evaluates the argument before calling connect. That's surely not what you want: your function returns None which causes the error.

There are other ways to do what you want, but the most direct fix is to create a partially evaluated function or a lambda expression which captures the value of file_path but does not yet perform the function. For example:

import functools

...

self.Button_save.clicked.connect(
        functools.partial(self.SaveTextFile,file_path))

This could fix your calling problem without changing the variables concerned.

However, note that this only works if file_path has the desired value at the time of calling the connect method. We can't tell if that's true in your code, but reading between the lines, probably not. So the accepted answer is probably more appropriate in your case.