IordanouGiannis
IordanouGiannis

Reputation: 4357

How do you bind a dynamically created widget to a signal in PyQt?

I have a table full of comboBoxes created by the push of a button.My question is how do you find out which comboBox changed ? If it was a fixed comboBox, I would use the following:

QtCore.QObject.connect(self.comboBox, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), self.dosomething)

I have added the example code below to make it more clear:

from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName(_fromUtf8("Dialog"))
        Dialog.resize(332, 122)
        Dialog.setMinimumSize(QtCore.QSize(332, 122))
        Dialog.setMaximumSize(QtCore.QSize(332, 122))
        self.tableWidget = QtGui.QTableWidget(Dialog)
        self.tableWidget.setGeometry(QtCore.QRect(10, 10, 256, 101))
        self.tableWidget.setObjectName(_fromUtf8("tableWidget"))
        self.tableWidget.setColumnCount(2)   
        item = QtGui.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtGui.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        self.tableWidget.setRowCount(0)
        self.layoutWidget_6 = QtGui.QWidget(Dialog)
        self.layoutWidget_6.setGeometry(QtCore.QRect(280, 30, 40, 54))
        self.layoutWidget_6.setObjectName(_fromUtf8("layoutWidget_6"))
        self.verticalLayout_2 = QtGui.QVBoxLayout(self.layoutWidget_6)
        self.verticalLayout_2.setMargin(0)
        self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
        self.pushButton_2 = QtGui.QPushButton(self.layoutWidget_6)
        self.pushButton_2.setAutoDefault(False)
        self.pushButton_2.setObjectName(_fromUtf8("pushButton_2"))
        self.verticalLayout_2.addWidget(self.pushButton_2)
        self.pushButton = QtGui.QPushButton(self.layoutWidget_6)
        self.pushButton.setAutoDefault(False)
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.verticalLayout_2.addWidget(self.pushButton)

        QtCore.QObject.connect(self.pushButton_2, QtCore.SIGNAL(_fromUtf8("clicked()")), self.add)

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_2.setText(QtGui.QApplication.translate("Dialog", "+", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setText(QtGui.QApplication.translate("Dialog", "-", None, QtGui.QApplication.UnicodeUTF8))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(QtGui.QApplication.translate("Dialog", "New Column", None, QtGui.QApplication.UnicodeUTF8))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(QtGui.QApplication.translate("Dialog", "New Column", None, QtGui.QApplication.UnicodeUTF8))

    def change(self):
        '''Depending on the comboBox whose index has changed,
         find the row and insert the right options in the comboBox
          in the next column'''

        #Find the row
        ins = self.tableWidget.focusWidget()
        selected_row = self.tableWidget.indexAt(ins.pos()).row()
        choice = self.tableWidget.cellWidget(0, selected_row).currentText()

        #Select the appropriate options
        if choice == 'B':
            choices_list = ['4', '5', '6']
        if choice == 'A':
            choices_list = ['1', '2', '3']

        #Set ComboBox in the next column
        comboBox = QtGui.QComboBox(self.tableWidget)
        font = QtGui.QFont()
        font.setPointSize(10)
        comboBox.setFont(font)

        for combo_ind, i in enumerate(choices_list):
            comboBox.addItem(_fromUtf8(""))
            comboBox.setItemText(combo_ind, QtGui.QApplication.translate("Dialog", i, None, QtGui.QApplication.UnicodeUTF8))

    def add(self):
        index = self.tableWidget.rowCount()
        self.tableWidget.insertRow(index)

        comboBox = QtGui.QComboBox(self.tableWidget)
        font = QtGui.QFont()
        font.setPointSize(10)
        comboBox.setFont(font)

        for combo_ind, i in enumerate(["A", "B"]):
            comboBox.addItem(_fromUtf8(""))
            comboBox.setItemText(combo_ind, QtGui.QApplication.translate("Dialog", i, None, QtGui.QApplication.UnicodeUTF8))     
        self.tableWidget.setCellWidget(index, 0, comboBox)

        comboBox = QtGui.QComboBox(self.tableWidget)
        font = QtGui.QFont()
        font.setPointSize(10)
        comboBox.setFont(font)

        #[1,2,3] is for A
        #[4,5,6] is for B
        for combo_ind, i in enumerate(['1', '2', '3']):
            comboBox.addItem(_fromUtf8(""))
            comboBox.setItemText(combo_ind, QtGui.QApplication.translate("Dialog", i, None, QtGui.QApplication.UnicodeUTF8))
        self.tableWidget.setCellWidget(index, 1, comboBox) 

app = QApplication(sys.argv)
app.setApplicationName('MyWindow')
window = QDialog()
ui = Ui_Dialog()
ui.setupUi(window)
window.show()
sys.exit(app.exec_())

Upvotes: 0

Views: 2690

Answers (2)

Aleksandar
Aleksandar

Reputation: 3661

You need to re-implement widget that you want to create dynamically. Basically this code is answer to this question, too.

Upvotes: 0

DMH
DMH

Reputation: 3975

One thing you might try is using a lambda, as in this similar question.

In your case it might look something like the following:

QtCore.QObject.connect(self.comboBox, QtCore.SIGNAL("currentIndexChanged(int)"), lambda index: self.dosomething(combo_id, index))

And you correspondingly change your self.dosomething method to take an extra parameter (say, an ID of some kind). This would allow you to pass on some identifying information about the event, i.e. which combo box it happened to, and as a bonus you get to re-use one method for all your components even as new ones are added.

Also note: In your case the signal is passing a value (int) to the slot. You need that information too, so you capture it as an argument for the lambda (above I have called it index ) and pass it to your slot method along with the identifying information ( combo_id ) about which combo box the signal is for.

If your signal doesn't send any parameters to the slot, such as for a button click, you need not use the lambda argument, like so:

QtCore.QObject.connect(self.button, QtCore.SIGNAL("clicked()"), lambda: self.dosomething(button_id))

Hope this helps.

Upvotes: 1

Related Questions