I think you can boil it all down to this: Where is your object created? If it's in "QThread::run()", then it got it's own concurrent OS-based thread. If not, then it's all in Qt's main GUI thread.
Even if you create a QThread object, its signals and slots basically "live" in the main thread (as in: the thread where you created the QThread).
QThread is not so much a real OS-thread, but more like "A wrapper / container which provides a method run, which will be executed in its own thread".
So you either go the "low level road", re-implement run(), preferrably don't call exec(), and do your processing. Data exchange will have to be protected (by mutexes, semaphores, etc.).
Or, you could leave run()'s default implementation (which calls exec()). Then you call, from your main (GUI) thread, .moveToThread() on some objects. Specifically: On those objects, whose events (slots) you want to have processed by another OS thread.
So if you have an QObject MyWorker, and a slot "doWork()", and then you do:
// now connect some signal to SLOT("doWork()") of someWorker
Then this means, whenever the doWork() slot is called, it will be executed in the context of it's own OS thread (the workingThread's thread).
I personally wouldn't mix those two. EITHER don't subclass QThread at all, don't touch its constructor etc. Do everything signal-slot based, and with .moveToThread().
OR, you do implement QThread and run(), but you don't call exec() and don't do .moveToThread with this specific class. Edit: or, if you have to, call exec() from this current QThread::run() context, but don't do moveToThread on other objects.
Just my 2 cents :-)