Zak44
Zak44

Reputation: 350

PySide \ PyQt: How to return value to UI after button click?

I am trying to return a path from a browse folder dialog box.

I have tried passing the instance object or even attribute to the call and setting it there:

self.browseBtn.clicked.connect(myExporter.browseFolder(self))

or

self.browseBtn.clicked.connect(myExporter.browseFolder(self.path))

But this doesn't work. It causes the browser dialog to pop open immediately upon load and then once you choose a folder it errors out with : Failed to connect signal clicked().

I have tried to set the clicked call to a return, with no luck:

result = self.browseBtn.clicked.connect(myExporter.browseFolder)

Can someone lead me in the right direction as far as how to return a value, when you are dealing with separate classes handling the UI and logic? Also... is it bad practice to be separating them like this? I know I could probably easily solve this if I threw everything into just one python file, but I figured that is not proper.

Here is my ui file (ui.py):

from PySide import QtCore, QtGui

class Ui_Dialog(object):
    def __init__(self):
        self.path =""

    def setupUi(self, Dialog, myExporter):
        Dialog.setObjectName("Dialog")
        Dialog.resize(382, 589)
        ...
        .......
        ............. 
        .................
        self.retranslateUi(Dialog)
        self.tabWidget.setCurrentIndex(1)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

        self.browseBtn.clicked.connect(myExporter.browseFolder)

Here is my exporter file (exporter.py):

class Exporter(object):
    def __init__(self):
        ...
        ......

    def browseFolder(self):
        ...
        .......
        do something
        ...........    
        return path

Here is my load/test file (loadExporter.py):

import ui as interface
import exporter as exporter
from PySide import QtCore, QtGui

app = QtGui.QApplication.instance()
if app is None:
    app = QtGui.QApplication(sys.argv)       
Dialog = QtGui.QDialog()
myExporter = exporter.Exporter()
myUI = interface.Ui_Dialog()
myUI.setupUi(Dialog, myExporter)
Dialog.show()
app.exec_()

Upvotes: 1

Views: 6678

Answers (2)

justengel
justengel

Reputation: 6320

It's not necessarily bad to have them in separate files. Having a separate file for certain widgets is a good thing especially if those widgets can be reused.

I would have my main file have a QMainWindow class.

class MyWindow(QtGui.QMainWindow):
    pass

if __name__ == "__main__":
    QtGui.QApplication([])

    mywindow = MyWindow()
    mywindow.show()

    sys.exit(QtGui.qApp.exec_())

Wrapping the app functionality in if __name__ == "__main__" prevents this code from being run when another file tries to import this file.

A signal (self.browserBtn.clicked) calls a callback method. Everything is an object in python.

def my_func():
    pass

new_func_name = my_func # my_func can be reassigned like any variable

my_func is an object. self.browseBtn.clicked.connect(my_func) passes my_func as a variable to be called later.

When self.browserBtn.clicked.emit() happens (on user click) it is the same as calling the connected functions my_func(). Other signals may pass values to callback functions self.lineEdit.textChanged.connect(my_func) calls 'my_func(new_text)'

You want your function to call everything for you.

def open_file(filename=None):
    """Open a file."""
    # If a filename isn't given ask the user for a file
    if filename is None:
        filename, ext = QtGUi.QFileDialog.getOpenFileName(None, "Open File", ".../My Documents/")
        # You may have to use the ext to make a proper filename

    # Open the file
    with open(filename, "r") as file:
        file.read()

self.browserBtn.clicked.connect(open_file)

Structure

...
import mywidget

class MyWindow(QtGui.QMainWindow):
    def __init__(self):
        super().__init__()
        ...
        self.show_file = QtGui.QLineEdit()
        self.setCentralWidget(self.show_file)

        self.exporter = mywidget.Exporter()
        self.browserBtn.clicked.connect(self.open_file)

    def open_file(self, filename=None):
        """Open a file."""
        path = self.exporter.browseFolder()
        # Do something with the path
        self.show_file.setText(path)

if __name__ == "__main__":
    QtGui.QApplication([])

    mywindow = MyWindow()
    mywindow.show()

    sys.exit(QtGui.qApp.exec_())

Upvotes: 1

Mr No Idea
Mr No Idea

Reputation: 11

I don't think I fully understand what you're trying to achieve, but may I suggest the following solution.

exporter.py

# implementation

dialog.py (Main UI)

import PyQt4.QtGui as qg
from exporter import Exporter

class Dialog(qg.QDialog):

    def __init__(self):
        super().__init__()
        self.path = None
        self.setup_widgets()

    def setup_widgets(self):
        self.browse.clicked.connect(self.on_browse_clicked)

    def on_browse_clicked(self):
        self.path = Exporter().browse_folder()

main.py

import sys
import PyQt4.QtGui as qg
from dialog import Dialog

if __name__ == '__main__':
    app = qg.QApplication(sys.argv)
    dialog = Dialog()
    dialog.show()
    sys.exit(app.exec_())

This way you still have three files, but dialog.py imports exporter.py instead of main.py importing both of them.

As for returning values from signals, I don't know; I think signals are voids (do not return values).

Signals pass arguments to slots via their formal parameters. Usually slots have the same or fewer parameters than their signal counterparts'. It's slots that return values.

Upvotes: 1

Related Questions