Connecting signal to slot from custom widget
-
I am trying to connect a signal to a slot when data is entered into a custom widget class:
class LineTabWidget(QWidget): lineDataUpdated = pyqtSignal(dict) def checkAndEmitAllData(self): if (self.node1XInput.text() and self.node1YInput.text() and self.node1ZInput.text() and self.node2XInput.text() and self.node2YInput.text() and self.node2ZInput.text() and self.elementLengthInput.text()): # Prepare the data lineData = { 'line_name': self.tabName, 'start_pos': {'x': self.node1XInput.text(), 'y': self.node1YInput.text(), 'z': self.node1ZInput.text()}, 'end_pos': {'x': self.node2XInput.text(), 'y': self.node2YInput.text(), 'z': self.node2ZInput.text()}, 'element_length': self.elementLengthInput.text() } # Save comprehensive line data self.saveLineData(lineData) # Emit the signal with all data self.emitLineData(lineData) print(f"Signal emitted and data saved {lineData}")
The data (dictionary) should be emitted to a controller which connects it to a slot:
class GUIController: """Controller class for the GUI. Connects signals to slots and handles the application logic.""" self.gui.linetabwidget.lineDataUpdated.connect(self.handleLineDataUpdated) def handleLineDataUpdated(self, lineData): print(f'Line data handled: {lineData}') start_pos = (float(lineData['start_pos']['x']), float(lineData['start_pos']['y']), float(lineData['start_pos']['z'])) end_pos = (float(lineData['end_pos']['x']), float(lineData['end_pos']['y']), float(lineData['end_pos']['z'])) element_length = float(lineData['element_length']) print(start_pos, end_pos, element_length) # Call the method in MainGUI self.gui.lineVTK(lineData['line_name'], start_pos, end_pos, element_length)
This approach has been working for other widgets like my custom Treeview, etc, with QActions and such.. I do get print statements that the signal is emitted with the dictionary in it, but it does not seem like handleLineDataUpdated is initiated when the signal is emitted (it is not connected to the slot)..
Anyone know why?
-
The LineTabWidget is initiated in another part of the code, the MainGUI class (main window).
class MainGUI(QMainWindow): self.linetabwidget = LineTabWidget()
Thus referred to gui.linetabwidget in the controller class. This should establish a connection between the lineDataUpdated signal of the LineTabWidget instance (self.gui.linetabwidget.lineDataUpdated) and the method (self.handleLineDataUpdated) designed to handle the data when the signal is emitted.
-
This is a working example:
class GUIController: """Controller class for the GUI. Connects signals to slots and handles the application logic.""" def __init__(self, gui): self.gui = gui # Connect signals to slots # Opening tabs in console from Treeview self.gui.treeview.openElementCB31InterfaceRequested.connect(self.openGUIElementCB31Tab) def openGUIElementCB31Tab(self): self.gui.openElementCB31Tab()
and my custom Treeview class:
class MyTreeView(QTreeView): """This class defines a custom tree view widget used in the MainGUI class that emits signals to the controller.""" openElementCB31InterfaceRequested = pyqtSignal() # Define signal for opening element CB31 interface tab in console def __init__(self, parent=None): super(MyTreeView, self).__init__(parent) def onItemDoubleClicked(self, index): item = self.model().itemFromIndex(index) if item: itemName = item.text() # Use .startswith() to check if the item name starts with "Line" if itemName.startswith('Line'): self.openLineInterfaceRequested.emit() # Use .startswith() to check if the item name starts with "CB31" elif itemName.startswith('CB31'): self.openElementCB31InterfaceRequested.emit()
and my MainGUI class:
class MainGUI(QMainWindow): """This class controls the main window of the application. It contains the main layout and widgets.""" # Setup custom TreeView self.treeview = MyTreeView() def openElementCB31Tab(self):
-
@MicVak said in Connecting signal to slot from custom widget:
self.linetabwidget = LineTabWidget()
This creates a new one and I don't see how that is the same as "Thus referred to gui.linetabwidget in the controller class". You may be right but you not have not shown a minimal example which demonstrates this.
If you have created a connection, you know the signal is fired and (it appears) the slot is not called then it seems to me the first thing to verify is that you are sure of the instances involved for signal and slot. Do a quick setObjectName(const QString &name) on both objects to make sure, at least for me?
If you want to rule out for sure anything about passing the
dict
parameter then temporarily remove this parameter from the signal and the slot. Because you are claiming the signal is emitted but the slot is not called, aren't you? -
I set:
class GUIController: """Controller class for the GUI. Connects signals to slots and handles the application logic.""" def __init__(self, gui): self.gui = gui print(f"LineTabWidget instance name: {self.gui.linetabwidget.objectName()}")
and:
class LineTabWidget(QWidget): lineDataUpdated = pyqtSignal(dict) def __init__(self, parent=None, tabName=""): super(LineTabWidget, self).__init__(parent) self.setObjectName("lineTabWidget")
which prints:
LineTabWidget instance name: lineTabWidget
I also removed the dict from the signal and added a new slot for handleLineDataUpdated that should only print if it recived signal. Nothing.
Atleast, when input is given to the LineTabWidget in the GUI i get:
Signal emitted and data saved {'line_name': 'Line 1 Model 1', 'start_pos': {'x': '0', 'y': '0', 'z': '0'}, 'end_pos': {'x': '20', 'y': '0', 'z': '0'}, 'element_length': '2'}
from
# Emit the signal with all data self.emitLineData(lineData) print(f"Signal emitted and data saved {lineData}")
-
@MicVak said in Connecting signal to slot from custom widget:
LineTabWidget instance name: lineTabWidget
Since you have put code in the constructor to always set the name of any
LineTabWidget
created to belineTabWidget
this tells us nothing.If you wish to post a minimal example of reproducible problem I will try it. "Minimal" means I can just copy & paste a box of lines and it runs. No external
.ui
s or anything like that. The fewest lines possible which exhibits the behaviour. If it's hundreds of lines then someone else may choose to look at it.BTW: I think it will make no difference, and is not relevant here, but just in case mark the slot with the
@pyqtSlot
attribute to make sure that is not an issue.