Python Pyqt Signals Are Not Always Working

Python PyQt signals are not always working

The problem is caused because the PyCalcCtrl object is not assigned to a variable so it will be destroyed and therefore the "_printthis" method will not be accessible. On the other hand, when functools.partial is used then the object of the PyCalcCtrl class is assigned to the scope of that function, that's why it works.

The solution is to assign the PyCalcCtrl object to a variable:

ctrl = PyCalcCtrl(model=model, view=view)

PYQT5 Signals & Slots are not working even though i followed the documentation

I do not know what documentation refers to the OP so I could not indicate if the tutorial you are following is correct or not. If you are referring to the official docs then you clearly have not followed it correctly.

Your code has many errors so I will only mention the most important ones:

  • The connection must be used before emitting the signal,

  • You should not inherit from QObject and QWidget since they will have conflicts, in addition to that there is no justification.

  • You shouldn't use pyqtSlot unnecessarily, setting it in the contstructor doesn't make sense.

  • You are creating a circular import.

  • "self.x" is not "mw" but another object.

A possible solution is the following:

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc

class bbb(qtw.QWidget):
def __init__(self):
super().__init__()

lay = qtw.QVBoxLayout(self)
self.label = qtw.QLabel()
lay.addWidget(self.label)
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
import slots

class aaa(qtw.QWidget, qtc.QObject):
submitted = qtc.pyqtSignal(str)

def __init__(self):
super().__init__()
self.edit = qtw.QLineEdit()
self.submit = qtw.QPushButton("Submit", clicked=self.onSubmit)
lay = qtw.QVBoxLayout(self)
lay.addWidget(self.edit)
lay.addWidget(self.submit)

self.myDialog2 = slots.bbb()
self.submitted.connect(self.myDialog2.label.setText)

def onSubmit(self):
self.submitted.emit(self.edit.text())
self.close()

self.myDialog2.show()

if __name__ == "__main__":
app = qtw.QApplication(sys.argv)
mw = aaa()
mw.show()
sys.exit(app.exec())

Why isn't this signal/slot code working

There is no real mystery here. Signal objects behave in exactly the same way as methods defined in classes.

If you put in some debugging prints like this:

class SignalFactory(QObject):
selectedTextureToLoad = returnASignal()
print(selectedTextureToLoad)

def foo(self): pass
print(foo)

class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
print(self.signals.selectedTextureToLoad)
print(self.signals.foo)
...

class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
print(self.signals.selectedTextureToLoad)
print(self.signals.foo)

and run your example, it will produce output like this:

Returning a signal
<unbound PYQT_SIGNAL []>
<function SignalFactory.foo at 0x7f2a57b1c268>
<bound PYQT_SIGNAL selectedTextureToLoad of SignalFactory object at 0x7f2a57b96828>
<bound method SignalFactory.foo of <__main__.SignalFactory object at 0x7f2a57b96828>>
<bound PYQT_SIGNAL selectedTextureToLoad of SignalFactory object at 0x7f2a57b96948>
<bound method SignalFactory.foo of <__main__.SignalFactory object at 0x7f2a57b96948>>
Emitting...

As you can see, both signals and methods are bound objects when accessed from instances, and unbound objects when accessed from the class. Methods must be bound to the instance so that self can be passed as the first argument. Likewise, a signal object must be bound to the instance to ensure that connected slots only receive signals from the specific instance that sent it.

So there are two signals named selectedTextureToLoad in your example - one for each instance of SignalFactory that is created. Your example doesn't work because the doSomeStuff slot isn't connected to the specific bound signal object that emitted the selectedTextureToLoad signal.

The signal-slot mechanism is designed for communication between objects. There is no facility for broadcasting messages without a known sender, so explicit connections must always be made between the emitter and the receiver. If you want to send a more generic signal, create a global instance of SignalFactory.

Python PyQt4 signal not firing connected method

If you want to see your callback being called, you have to change the way you connect
your QThread signal to your slot:

 QtCore.QObject.connect(self.worker,
Qt.SIGNAL("a"),
self.Tester,
Qt.Qt.DirectConnection) #this is added

Explanation:
Qt signals/slots are thread safe since Qt 4. Documentation says the normal behaviour when
connecting a signal to a slot is 'AutoConnection', which means it is direct (callback is
fired immediately after emit) if both signal and slot are in the same thread, whereas it
is queued if the signal is emitted from another thread (your case).

Then, something has to process this queue: normally, this is done by the Qt loop while your application is running.

The fact that a signal is "queued" means control is transferred to the Qt loop, to make
sure the callback is called in the main thread even if the signal comes from another
thread.

In your question's code, there is no event loop at all - so the only way of getting your
slots called from another thread is to change the way signals are dispatched. Don't use
that in your code, though, since in a normal application you have the Qt loop running,
it would have been dispatched properly (and safely from threading issues).

PyQt5: The connected slot to a clicked signal not working

The problem is that you didn't create a persistent object for the controller instance, so the instance is immediately garbage collected afterwards because it isn't referenced anywhere else.

As long as a reference to the instance exists, it will work as expected.

In this case a local variable will suffice, since app.exec() will block further processing within main, ensuring that the instance will exist until it exists.

def main():
app = QApplication(sys.argv)
view = UserInterface()
view.show()

controller = Controller(view=view)

sys.exit(app.exec())

Nonetheless, let me tell you that while conceptually using an MVC is usually a good idea, you should use it carefully, without "exaggerating" the pattern and only if it really helps the development.

I'd like to point up one of the disadvantages of MVC:

Lack of incremental benefit – UI applications are already factored into components, and achieving code reuse and independence via the component architecture, leaving no incremental benefit to MVC.

I understand that yours is a simple and conceptual example, but it certainly is an overly complicated one for what it does. For instance:

  • you don't seem to be using a model, or at least it doesn't seem that you need a full MVC pattern to do that (one should choose an MVC whenever there's actual advantage in doing so, not "because you should use it");
  • using an MVC pattern doesn't necessarily mean that you have to use 3 classes (and 3 separate files) which might also decrease the code navigability; MVC is mostly about the way the three elements are divided in the program logic: Qt (as most frameworks that provide UI elements) already helps with that, since it provides ui elements that do almost nothing besides showing themselves, and other elements that allow interacting with them (file access, network interfaces and actual models);
  • overusing functions to create the UI is usually a bad idea, as it makes things much more complicated than they are, most importantly from the point of view of readability: while using separate functions might help in keeping your code "tidier", functions are also created for their reusability, and in your UserInterface class there are 4 functions that will most certainly be used only once.


Related Topics



Leave a reply



Submit