1

I have a PySide2 GUI application with a QPushButton button with a @Slot function connected to it. How can I share data with the function?

from PySide2.QtCore import Slot
from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QVBoxLayout

@Slot()
def button_XYZ_callback():
    # Function which is executed when the button XYZ is clicked.
    # I'd like to access the __main__s context data "parent_data" here.
    pass

if __name__ == '__main__':
    # parent context data what I want to access (read only)
    parent_data = "blub"

    application = QApplication(sys.argv)
    window = QMainWindow()
    central_widget = QWidget()
    xyz_button = QPushButton("XYZ", central_widget)
    xyz_button.clicked.connect(button_xyz_callback)
    layout = QVBoxLayout(central_widget)
    layout.addWidget(xyz_button)
    window.show()
    sys.exit(application.exec_())
thinwybk
  • 4,193
  • 2
  • 40
  • 76
  • Can you post a [minimal example](https://stackoverflow.com/help/minimal-reproducible-example) of what you are trying to achieve? – Heike Aug 23 '19 at 14:12
  • Per Python's [LEGB rule](https://stackoverflow.com/q/291978/190597), the global variable `parent_data` is accessible from within the `button_XYZ_callback` function. – unutbu Aug 23 '19 at 16:32
  • @unutbu I know but global variables are usually considered bad design. I wondered if the is some other pyside specific meachnism I don't know of yet. – thinwybk Aug 23 '19 at 16:40

1 Answers1

2

Per Python's LEGB rule, the global variable parent_data is accessible from within the button_XYZ_callback function.

If, however, you wish to reduce the function's dependence on global variables, the standard technique is to define a class, and use class or instance attributes to store what was before global values:

# based on code from https://wiki.qt.io/Qt_for_Python_Tutorial_ClickableButton
import sys
from PySide2 import QtCore, QtWidgets, QtGui


class MyWidget(QtWidgets.QWidget):
    def __init__(self, data):

        QtWidgets.QWidget.__init__(self)
        self.data = data
        self.button = QtWidgets.QPushButton("Click me!")
        self.text = QtWidgets.QLabel("Hello World")
        self.text.setAlignment(QtCore.Qt.AlignCenter)
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.text)
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)
        self.button.clicked.connect(self.button_XYZ_callback)

    @QtCore.Slot()
    def button_XYZ_callback(self):
        print(self.data)

if __name__ == "__main__":
    parent_data = "blub"
    app = QtWidgets.QApplication(sys.argv)
    widget = MyWidget(data=parent_data)
    widget.show()
    sys.exit(app.exec_())

Alternatively, if the data is known before the callback is to be defined, you could use a function factory to place the data in the enclosing scope of the callback:

import sys
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QApplication, QPushButton


def make_callback(data):
    @Slot()
    def button_XYZ_callback():
        print(data)
    return button_XYZ_callback

if __name__ == "__main__":
    parent_data = "blub"
    # https://wiki.qt.io/Qt_for_Python_Tutorial_ClickableButton
    app = QApplication(sys.argv)
    button = QPushButton("Click me")
    button.clicked.connect(make_callback(parent_data))
    button.show()
    app.exec_()
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Is it reasonable to define `class MyWindow(QMainWindow)` in addition to `class MyWidget(QtWidgets.QWidget)` and to pass the window instance into the widget during construction? – thinwybk Aug 26 '19 at 06:49
  • Widgets should define methods for everything that the widget can do, but need not know anything about the larger world of the Window. The Window should know about the widgets it contains and define how they interact. Organized this way, a Widget could be used by more than one window or application in possibly different ways. – unutbu Aug 26 '19 at 09:44
  • 1
    This separation of responsibilities helps keep the code organized, maintainable, extensible and reusable. Thus, it is [more usual](https://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html) to create instances of Widgets inside the Window's `__init__` rather than passing a window instance to the Widget's `__init__`. – unutbu Aug 26 '19 at 09:44
  • I am creating instances of Widgets inside the Window's `__init__` already. Just wondered if there are other patterns. Thx. – thinwybk Aug 26 '19 at 10:01