0

I have a fairly complicated piece of code using PyQt4 and sometimes my signal callbacks simply never run.

It seems to be related to where/when I register the callbacks, ie. self.someobj.somesig.connect(self.callback).

I can see in the debugger that the callback is connected and that the signal es emitted.

The code is too large to post here, but I will try to summarize:

class RC(QtCore.QObject):
  render_ready = QtCore.pyqtSignal(object)
  def __init__(self, pc, ...):
    super(RC, self).__init__()
    self.pc = pc
    self.pc.rr.new_render.connect(self.insert)
  def check(self):
    ...
    if a: self.pc.replot_request.emit()
    else: self.pc.render_request.emit()
  def insert(self, r):
    ...
    if b: self.render_ready.emit(r)
    self.check()

class RR(QtCore.QObject):
  new_render = QtCore.pyqtSignal(object)
  def __init__(self, pc, app,...):
    super(RR, self).__init__()
    self.app = app
    self.pc = pc
  def run(self):
    self.pc.replot_request.connect(self.on_replot_request)
    self.pc.render_request.connect(self.render)
  ...
  def render(self, scale):
    ...
    r = R(i)
    r.moveToThread(QtGui.QApplication.instance().thread())
    r.new_render.emit(r)

class R(QtCore.QObject):
  def __init__(self, i):
    super(R, self).__init__()
    ...
  ...

class PC(QtCore.QObject):
  render_request = QtCore.pyqtSignal(int)
  replot_request = QtCore.pyqtSignal(object)
  def __init__(self, container, app):
    super(PC, self).__init__()
    ...
    self.rr = RR(self.app)
    self.rr_thread = QtCore.QThread()
    self.rr.moveToThread(self.rr_thread)
    self.connect(self.rr_thread, QtCore.SIGNAL("started()"), self.rr.run)
    self.c = RC(self)
    ...
    self.c.render_ready.connect(self.on_nr)
  def start(self):
    self.rr_thread.start()
  def on_nr(self, r):
    print "on_nr(...)"

class App(QtCore.QObject):
  ...
  def __init__(self):
    ..
    self.pc = PC(container, app)
    ...
    self.pc.start(self)  # Last thing in constructor

X = QtGui.QApplication(sys.argv)
a = App()
sys.exit(X.exec_())

I see render_ready being connected to on_nr and the render_ready being emitted. But on_nr never runs. The signal sequence is complicated, it goes across threads, but the signal and callback in question are in the same thread. Also, the event loop is clearly running. How could I start digging deeper? Or am I making some evident mistake? Thanks.

jpcgt
  • 2,138
  • 2
  • 19
  • 34
  • 1
    I think you need to create a [MCVE]. Right now there are too many unknowns with your code (for instance in class `RC` you reference `self.pc.r` but I can't see that defined anywhere. In `RR.render` you create a new instance of `R` and emit a signal, but you don't have the definition for `R` anywhere) – three_pineapples Jan 10 '16 at 02:52
  • @three_pineapples If I could create an [MVCE](http://stackoverflow.com/help/mcve) it would mean I'm almost through solving the problem myself. Right now I'm in the dark as to what could be causing this, so I really don't know how to create a standalone recreation of the problem. By the way, `R` is just a simple subclass of `QtCore.QObject`. I'm updating the example now. – jpcgt Jan 10 '16 at 05:36
  • Ok, well I still don't understand `RR.render`. You create an instance of `R`, move it to a different thread (why?) and emit a signal that doesn't appear to be connected to anything. Meanwhile, `RC.render_ready` isn't emitted until `RC.insert` is called, which happens when `PC.r.new_render` is emitted, but that object (`PC.r`) doesn't seem to exist in your code. So I'm not sure if you have simplified your code too much, or if this is the root cause of your problem. I know making a [MCVE] will mean you are likely to solve your problem, but quite frankly, that is the point. – three_pineapples Jan 10 '16 at 22:12
  • 1
    So my answer to the question in the title "how to debug?" is make a [MCVE]. You'll either realise you did something silly, or manage to recreate the problem in which case someone here will be able to explain whatever subtlety of signals, slots and threads you have uncovered. – three_pineapples Jan 10 '16 at 22:14
  • @three_pineapples I fixed the code. `PC.r` changed to `PC.rr`. I was able to solve the problem and unfortunately it had nothing to do with PyQt and the use of signals. I tried an MVCE, but as I suspected would happen, the problem did not show up, because I did not include the cause... I had not pinpointed it yet. See my answer below. Thanks anyway. – jpcgt Jan 11 '16 at 19:31

1 Answers1

0

Unfortunately it was a silly mistake as suggested in the comments to the question. It had nothing to do with PyQt or the use of signals. I post what the problem was and how I debugged it in hopes that it still might help somebody.

What the problem was

I was overwriting the object that emitted the signal with a new one (an attempt to reset state), so its signal was not connected to the callback any more.

How it was detected

In the debugger I noticed that the address of a.pp.rr had one value when I connected the signal to it, and when the signal was emitted, it's address was different. Two different objects.

So the code was clean and the use of PyQt signals was correct. Perhaps the lesson learned is to be careful when replacing an instance. Make sure to reconnect all signals to/from the new instance, or create a method in the object to reset it's state (this is what I did) to avoid destroying the original instance.

Other Options

With regards to creating an Minimal, Complete, and Verifiable example as suggested earlier, it is perhaps the most appropriate way to format a question in this forum and a good exercise in identifying the source of problems. Equally valid, and perhaps more appropriate for more obscure and complex problems is through the skillful use of the debugger. If you are coding A to be connected to B and A is not having and impact on B, then it is possible that A is not the same "A" or B is not the same "B" as when the "connection" was made, or the connection was removed at some point. When you are missing something, perhaps you want is to gain more visibility into what is happening.

Community
  • 1
  • 1
jpcgt
  • 2,138
  • 2
  • 19
  • 34