1

In my PyQt5 program, I have tabs and use one function for creating the same tab multiple times. In PyQt, to run code when a button is clicked you need to connect it to another function. In the createATab function, I have a QLineEdit which needs to be edited when a button is clicked. Here's an example of what I am trying to do:

class Example(QWidget, QWindow):
    def __init__(self):
        super().__init__()

        self.initUI()
    def runThisWhenButtonClicked(self):
        getText = editThisLine.text()
        editThisLine.clear() # edits variable in other function except other function is run multiple times
    def addTheTab(self, username):
        addingTab = QWidget()
        aButton = QPushButton("Stuff")
        editThisLine = QLineEdit()
        aButton.clicked.connect(self.runThisWhenButtonClicked)
        tabGrid = QGridLayout(addingTab)
        tabGrid.addWidget(editThisLine, 3, 1, 4, 2)
        tabGrid.addWidget(aButton, 3, 3, 4, 3)
        tab_widget.addTab(addingTab, str(username))    

    def initUI(self):
        global tab_widget
        tab_widget = QTabWidget()
        listwidget = QListWidget()
        listwidget.itemDoubleClicked.connect(self.addTheTab)
        splitterrr = QSplitter()
        splitterrr.addWidget(listwidget)
        splitterrr.addWidget(tab_widget)
        QListWidgetItem("Test1", listwidget)
        QListWidgetItem("Test2", listwidget)
        mainLayout = QHBoxLayout()
        mainLayout.addWidget(splitterrr)
        self.setLayout(mainLayout)
        self.setGeometry(300, 300, 300, 300)
        self.setWindowTitle('Test')
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

How do I pass a variable by reference?

I have read through this and although it does explain a lot, it doesn't explain how to solve this issue.

pyqt4 button click handler

This explains how you are only passing the function, not calling it. So, passing variables to the runThisWhenButtonClicked function is not possible.

Community
  • 1
  • 1
user4884072
  • 81
  • 3
  • 10
  • Short version: use `self.editThisLine`. Longer version: [read up on classes, instances, and instance properties](https://www.jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/). – rmunn Sep 30 '15 at 12:38
  • About the short version - returned an error: "'Example' object has no attribute 'editThisLine'. About the longer version - thanks for the link! – user4884072 Sep 30 '15 at 12:47
  • You have to use `self.editThisLine` everywhere -- first to set it, and then to reference it. Instead of creating a variable that's local to one function, you're creating an instance attribute. That link should help explain what's going on. – rmunn Sep 30 '15 at 12:59
  • I added self. in front of all editThisLine,it now edits the other tab instead of the tab the button was clicked. I'll read more of the article once I get more time soon. – user4884072 Sep 30 '15 at 13:04

1 Answers1

1

The concept you are missing is closure. A closure wraps up a scope with a function, so that code in the function can access variables within that associated scope. The simplest way to utilise closures when connecting signals is with a lambda (although some people prefer to use functools.partial).

In your example, this would mean connecting the signal like this:

    aButton.clicked.connect(lambda: self.runThisWhenButtonClicked(editThisLine))
    ...

def runThisWhenButtonClicked(self, editThisLine):
    print(editThisLine.text())

However, I think this may be "papering over the cracks" in your code, and is possibly not the best solution. A better solution would be to make proper use of the namespaces that classes provide. The addTheTab and runThisWhenButtonClicked should be refactored into a Tab class which has all its child widgets as attributes:

class Tab(QWidget):
    def __init__(self, parent=None):
        super(Tab, self).__init__(parent)
        self.editThisLine = QLineEdit()
        self.aButton = QPushButton("Stuff")
        self.aButton.clicked.connect(self.runThisWhenButtonClicked)
        ...

    def runThisWhenButtonClicked(self):
        print(self.editThisLine.text())

This would completely eliminate the need for closures in your example.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336