PDA

View Full Version : connect, disconnect and reconnect problem



janK
22nd August 2010, 11:24
Hello

In my application I have a problem with REconnecting signal/slots.
For simplicity, assume I have three objects, GUI, Processor and backend.
GUI is obviously the gui, processor processes streams of data coming from
my soundcard and backend is a postprocessor for some of this data.
For a variety of reasons I sometime have to delete the backend and instantiate a new version with completely different parameters.

Logically the gui does not "see" the pointer to backend, so for the creation of the signal/slot connections, I pass reference to the gui object through the processor to this backend. On instantiation I create/recreate a number of signal/slot connections between the backend and (elements of the) gui.

Now, on creation (first time) of the backend all connections are fine, on REcreation of the backend object, the connection instantiated through connect
(this, SIGNAL (..), MyGui, SLOT (...))
work fine, the ones though created through
connect (MyGui, SIGNAL (...), this, SLOT (...))
fail though. I.e. the signals from the gui apparently do not bind to the slots of the newly created backend anymore, and the buttons and sliders on the gui have no effect on the backend operation.

If I simplify recreation of the backend by NOT destroying it, i.e. just creating another
instance of the backend, disregarding the old instantiation, everything works fine.

I apologize for the lengthy story, my question is whether or not I am forgetting something?

yakin
22nd August 2010, 11:58
Does the connect always returns true? Any messages on the console concerning the connect call? Can you please post the concerned code parts?

In principle the connect should work like you expect.

janK
22nd August 2010, 12:26
That is what I was assuming. The connects give "TRUE", in all cases

The code is essentially what I gave, a series of connects involving a pointer, slot and signal

So, may be I am overlooking something with the construct or the basic semantics

best
jan

yakin
22nd August 2010, 12:39
If I simplify recreation of the backend by NOT destroying it, i.e. just creating another
instance of the backend, disregarding the old instantiation, everything works fine.

I believe, this has nothing to do with the problem at all. Not destroying the backend just means, that the signals from the gui will be processed by the old, not deleted backend. So it may look like everything is fine, but it is not. (Compare the this pointer in the slot with the newly created backend).

Greetz Yakin

janK
22nd August 2010, 14:59
In the case of not deleting the old backend, this "old" backend is not reachable anymore, the "this" in the connect of the new backend is definitely different from the "this" in the connect of the first instantiation of the backend. They are absolutely different pointers, so I strongly believe
that with the destruction of the backend object something went wrong.

What strikes me most is that the binding in
connect (MyGui, SIGNAL (...), this, SLOT (...))
does NOT have the expected result, while in the same context
connect (this, SIGNAL (...), MyGui, SLOT (...))
does. It does not make sense to me.

So, I am lost and for the time being my simple app has a commented line in the
processorcode:
// delete Mybackend
Mybackend = new Decoder (MyGui, ...);
but the "solution" is rather unsatisfactory.
best
jan

janK
22nd August 2010, 15:34
additional info:
1. behaviour is the same under win7 (Qt/mingw), fedora 13-64 and ubuntu 10.04 (64bits).
2. in case of destroying the object before reinstantiation, the pointers to the old and new object
are the same (obviously not when not destroying the old backend object)

best jan

hobbyist
22nd August 2010, 16:16
Have you tried QObject::setObjectName() and QObject::dumpObjectInfo() to debug/monitor the active connections?

Anyways, a small self-contained example to reproduce the problem would be nice.

janK
22nd August 2010, 17:12
Answer: no, no
It is part of a 15000 lines program system, I'll try to build a small system with the observed
behaviour

best
janK

janK
23rd August 2010, 21:07
I was just constructing a small subset of my program when I realized that "Processor" as mentioned in my first description is a running thread.
So Gui is the gui, Processor a separately running thread and backend just an object instantiated in the processor.
Is it possible/safe to connect from within backend
connect (MyGui, SIGNAL (...), this, SLOT (...)),
then
disconnect (MyGui, SIGNAL (...), this, SLOT (...))
and then reapply connect (MyGui, SIGNAL (...), this, SLOT (...))
again from an object, instantiated from a thread (Processor)?

Where Processor contains code like

if (cond) {
delete MyBackend;
MyBackend = new Backend (...)
}

best jan

janK
24th August 2010, 13:59
It took me some time isolating the relevant parts.
The detector is an object, depending on parameter settings (omitted here), created and recreated. On recreation it attempts to connect itself to the GUI (MyRadioInterface), on destruction it disconnects
#

#include "gui.h"
#include "detector.h"

void Detector::test (int n) {
fprintf (stderr, "int is %d\n", n);
}

Detector::Detector (RadioInterface *RI) {
int t;
_WorkingRate = 9600;
MyRadioInterface = RI;

t = connect (MyRadioInterface, SIGNAL (setting_Balance (int)),
this, SLOT (test (int)));
fprintf (stderr, "creating new detector, connect yields %d\n", t);
}

Detector::~Detector () {
disconnect (MyRadioInterface, SIGNAL (setting_Balance (int)),
this, SLOT (test (int)));
}

complex Detector::processSample (complex sample) {
return complex (0, 0);
}

The main processor of the radio instantiates (a.o.) the detector

#include "gui.h"
#include "doradio.h"

#define WILL_START 01012
#define RUNNING 01013
#define WILL_EXIT 01014
#define NONE 01111
#define SET_NEW_WORKING_RATE 01001
extern complex ComplexZero;

doRadio::doRadio (RadioInterface *RI) {
MyRadioInterface = RI;

_WorkingRate = 9600;
ModeofOperation = WILL_START;
ChangeComponent = NONE;

connect (RI, SIGNAL (setting_Halt (void)), this, SLOT (setHalt (void)));
connect (RI, SIGNAL (setting_WorkingRate (int)), this, SLOT (setWorkingRate (int)));
MyDetector = new Detector (MyRadioInterface);
}

doRadio::~doRadio () {
}

void doRadio::setHalt () {
ModeofOperation = WILL_EXIT;
}

void doRadio::setWorkingRate (int wr) {
_WorkingRate = wr;
ChangeComponent = SET_NEW_WORKING_RATE;
}

/*
* The actual process
*/
void doRadio::run () {
complex sample;
int ExitCondition = 0;

while (!ExitCondition) {
switch (ChangeComponent) {
case SET_NEW_WORKING_RATE:
delete MyDetector; //THIS LINE AFFECTS BEHAVIOUR
MyDetector = new Detector (MyRadioInterface);
ChangeComponent = NONE;
break;

default:
break;
}
switch (ModeofOperation) {
case WILL_START:
ModeofOperation = RUNNING;
break;

case WILL_EXIT:
ExitCondition = 1; // exit the loop and die
break;

case RUNNING:
sleep (10);
sample = MyDetector -> processSample (ComplexZero);
break;
}
}
}

I just connected the detector object with something on my gui (a slider here),
when given as above, the decoder reports "1" as result of the connection, however
moving the slider does not result in a signal sent (only the first time)
When commented out the line
delete MyDetector
I can send signals "setWorkingRate" here and the decoder not only
reports "1" as result of the connection, but the gui slider is effectively connected
to the "test" method, and lots of numbers will be printed

Anyone with some idea???
(or formulated differently: help urgently needed)

best jan

yakin
24th August 2010, 14:43
You construct the first Detector in the constructor of your thread class. That means, this object is part of the main thread because your thread (doRadio) is not started yet.
Then you delete the detector from the running thread (doRadio) where the detector object belongs to the main thread. After that, you construct a new detector in the doRadio thread (So the new object belongs to the doRadio thread).
Qt distinguish between queued connection and normal connection. Queued connection will work between threads, normal won't. If you do not specify a connection type while connecting (like you did), the connection type will be auto. In this case Qt uses QueuedConnection between different threads. So far...
After the delete, you will get a QueuedConnection. This means, your thread needs to dispatch events in your run method to process the signals from the main thread. But you do not dispatch the event with an event loop. You have to call QThread::exec in doRadio::run() to use the threads event loop or you can create your own event loop, cause QThread::exec() will only return after exit() called. That would be hard to bring together with your while loop. :(

janK
24th August 2010, 15:43
OK, I see the point. The good thing is to realize that it is indeed my code that is erroneous, the bad news
is that I have to think really hard for a good solution, I'll study on Queued Connections.
Thanks a lot

best jan

yakin
25th August 2010, 13:39
Hi Jan,

I forget to tell you. You can try to move the object with moveToThread(QThread* targetThread) to your main thread before you do the connect. Not perfect but should work.

janK
25th August 2010, 19:25
Actually, I changed the program such that the detector object is created
and managed by the main thread.
Thanks anyway for the suggestion, I do realize that I still have to learn a lot
on the details of Qt

Thanks for your efforts, appreciated

best jan