How to access data from thread two, created in thread one
-
bq. The other alternative is to give a pointer to your data in animation thread to your sand thread but be careful with data access (see QMutex for this).
Well, I tried this but this didn't work, maybe I did it wrong :-)
How I did it:
@
// In MainWindow
QByteArray *byteArray
aThread = animationThread(byteArray);
sThread = sendThread(serial,byteArray);
@In the animationThread the address to byteArray is passed to
to the animation class
On runtime I got a warning something like cannot create Child in parent or so. However it did not work!
Maybe because the animation class does not inherit QObject?@class Animations : public Draw
{
public:
Animations();// QByteArray getSendCube(void);
/************************************************************
* EFFECTS FUNCTION PROTOTYPES
************************************************************/
void effectWall(Axis axis, Direction direction, uint16_t speed);
void effectTest(void);
void effectRain(uint16_t iterations,uint16_t speed);
void effectLift(uint16_t iterations, uint16_t speed, uint16_t delay);
void effectRandomZLift(uint16_t iterations, uint16_t speed);
void effectFirework(uint16_t iterations, uint16_t speed, uint8_t particles);
void effectWireBoxCornerShrinkGrow(uint16_t iterations, uint16_t speed, uint8_t rotate,
uint8_t flip);
void effectWireBoxCenterShrinkGrow(uint16_t speed, Bool centerStart);
void effectAxisNailWall(uint16_t speed, Axis axis, Bool invert);
void effectLoadbar(uint16_t speed, Axis axis);
void effectRandomSparkFlash(uint16_t iterations, uint16_t speed,
uint16_t bixels);
void effectRandomSpark(uint16_t sparks, uint16_t speed);
void effectRandomFiller(uint16_t speed, BixelState state);
void effectString(uint8_t *str, uint16_t speed);
// void ripples (int iterations, int delay);
//float distance2d (float x1, float y1, float x2, float y2);private:
void animationWait(uint16_t speed);
void sendBixelZ(uint8_t x, uint8_t y, uint8_t z, uint16_t speed);
void effectZUpDownMove(uint8_t destination[CUBE_ARRAY_SIZE],
uint8_t position[CUBE_ARRAY_SIZE], Axis axe);// uint8_t sendCube[CUBE_SIZE][CUBE_SIZE];
QTimer *timer;
// QByteArray *sendData;
// QReadWriteLock lock;
// bool stop;
};
@@class Draw
{
public:
Draw();
/**
* @brief Boolean Type definition
*//*********************************************************************** * @brief Two dimensional array for cube data * cubeFrame[z][y] |= (0x01 << 0) ; where z=y=0 * which turns on the first LED at position 000 (x,y,z - Coordinate) * or in other words first layer, first data bit, and first row is * activated. ***********************************************************************/ uint8_t cubeFrame[CUBE_SIZE][CUBE_SIZE]; // [z][y] uint8_t cubeFrameTemp[CUBE_SIZE][CUBE_SIZE]; // [z][y] void setBixel(uint8_t x, uint8_t y, uint8_t z); void setTempBixel(uint8_t x, uint8_t y, uint8_t z); void clearBixel(uint8_t x, uint8_t y, uint8_t z); void clearTempBixel(uint8_t x, uint8_t y, uint8_t z); BixelState getBixelState(uint8_t x, uint8_t y, uint8_t z); void flipBixels(uint8_t x, uint8_t y, uint8_t z); void alterBixel(uint8_t x, uint8_t y, uint8_t z, BixelState state); Bool inRange(uint8_t x, uint8_t y, uint8_t z); void shift(Axis axis, Direction direction); void checkArgumentOrder(uint8_t from, uint8_t to, uint8_t *newStartPoint, uint8_t *newEndPoint); void drawPositionAxis(Axis axis, uint8_t position[CUBE_ARRAY_SIZE], Bool invert); uint8_t flipByte(uint8_t byte); void setPlaneZ(uint8_t z); void clearPlaneZ(uint8_t z); void setPlaneX(uint8_t x); void clearPlaneX(uint8_t x); void setPlaneY(uint8_t y); void clearPlaneY(uint8_t y); void setPlane(Axis axis, uint8_t i); void clearPlane(Axis axis, uint8_t i); void boxWireframe(uint8_t x1, uint8_t y1, uint8_t z1, uint8_t x2, uint8_t y2, uint8_t z2); void boxFilled(uint8_t x1, uint8_t y1, uint8_t z1, uint8_t x2, uint8_t y2, uint8_t z2); void boxWalls(uint8_t x1, uint8_t y1, uint8_t z1, uint8_t x2, uint8_t y2, uint8_t z2); void mirrorX(void); void mirrorY(void); void mirrorZ(void); // @brief Function Prototypes for filling cube array void fillTempCubeArray(uint8_t pattern); void fillCubeArray(uint8_t pattern); uint8_t byteline(uint8_t start, uint8_t end); void tmpCubeToCube(void); void fontGetChar(uint8_t chr, uint8_t dst[5]);
private:
void wait();
};
@ -
bq. The easiest and safest way is using the SIGNAL – SLOT mechanism. Emit a signal with your QByteArray as parameter in animation thread and connect to a slot taking a QByteArray as parameter in your sent thread.
Sounds promising, but there is a problem the animation generation takes some time because every animation waits at least ones for X milliseconds before it continues creating the next part of the animation, so in fact I have to emit a signal within the animation class
because when it returns to the animationThread the data are already X times overwritten. -
I get the following warning (error) on runtime.
@QObject::connect: Cannot queue arguments of type 'QByteArray&'
(Make sure 'QByteArray&' is registered using qRegisterMetaType().)
@How can I solve this!
-
I get this warning when calling the sendAnimations function in SendThread.
@QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x151ecd0), parent's thread is QThread(0x14be8f0), current thread is QThread(0x7fff1a0dda50)
@What does that mean should I open the serial port in the SendThread instead in MainWindow class?
-
Hi,
bq. // In MainWindow
QByteArray *byteArray
aThread = animationThread(byteArray);
sThread = sendThread(serial,byteArray);That kind of construct is not safe at all.
Since you will be sending QByteArrays in your SenderThread, just emit one from your AnimationThread
-
Well, I cant emit from AnimationaThread (I think at least :-) ), from this class all animation are called which are in the class animation.
In each animation the wait function gets called at least ones.
where than I emit currently a signal connected to the SenderThread.I changed QByteArray to QVector< QVector<uint8_t> >
That's how I do it right now
@// in MainWindow create connection
connect(playAction,&QAction::triggered,animationThread,&AnimationThread::createAnimations);
connect(animations,&Animations::dataChanged,sendThread,&SendThread::sendAnimations);
// How the worker threads are right now created
animations = new Animations;
animations->moveToThread(&aThread);
animationThread = new AnimationThread(animations);
animationThread->moveToThread(&aThread);
aThread.start();
serial->moveToThread(&sThread);
sendThread = new SendThread(serial);
sendThread->moveToThread(&sThread);
sThread.start();
@@ // In animationThread call animation from class animation
animationFirework(....);
@@// in the animation class
void animationFirework(...)
{
...
wait(ms);
....
wait(ms);
}void wait(uint16_t ms)
{
emit dataChanged(cubeVec); // void dataChanged(QVector< QVector<uint8_t> > &cubeVec)
timer->start(ms)
while(timer->isActive());
}
@@ // in class SenderThread
void SendThread::sendAnimations(QVector< QVector<uint8_t> > &data)
//void SendThread::sendAnimations()
{
// QReadLocker locker(&lock);// while(true){
m_serial->putChar(0xFF);
m_serial->waitForBytesWritten(1000);
m_serial->putChar(0x00);
m_serial->waitForBytesWritten(1000);
for (int z = 0; z < CUBE_SIZE; z++) {
for (int y = 0; y < CUBE_SIZE; y++) {
m_serial->putChar(data[z][y]);
m_serial->waitForBytesWritten(1000);
if(data[z][y] == 0xFF){
m_serial->putChar(0xFF);
m_serial->waitForBytesWritten(1000);
}
}
}
if (m_stoped)
return;
// m_running = true;
}
@And get the following warrning(error)
QObject::connect: Cannot queue arguments of type 'QVector<QVector<uint8_t> >&'
(Make sure 'QVector<QVector<uint8_t> >&' is registered using qRegisterMetaType().) -
Ok the problem with qRegisterMetaType() is solved
-
You can also Mutexes / ReadWriteLocks to guard your data.
I suggest creating a helper template class, something like "GuardedData<class T>". In there you have your actual data member of type T, and a ReadWriteLock. You only provide two public methods, "T get()" and "set(T val)". Inside the get()mMethod, you lock the ReadWriteLock for reading, and inside the set()-method, you look it for writing...
Having this helper-class, you could easily create a "GuardedData<QByteArray> m_guardedByteArray" member, that both threads can use. Via the ReadWriteLock, accesses are safe (and serialized).
There can obviously be a performance penalty, but depending on the size of your data and the frequency of access, it might not be noticable at all...
You can of still use the signal-slot-approach...
-
I have created this struct in a global header file and want to use it for singal and slots. But it does not work!
In Global.hpp
@typedef struct{
QString name;
QString text;
uint8_t id;
uint8_t particle;
uint16_t speed;
uint16_t delay;
uint16_t leds;
uint16_t iteration;
Direction direction;
Axis axis;
Bool invert;
BixelState state;
}AnimationStruct;
Q_DECLARE_METATYPE(AnimationStruct)@In MainWindow.cpp
@ qRegisterMetaType<AnimationStruct>("AnimationStruct");@
Error:
@ no matching function for call to 'QObject::connect(const Object*&, void (MainWindow::&)(const AnimationStruct&), const Object&, void (AnimationThread::*&)(const AnimationStruct&), Qt::ConnectionType)'
return connect(sender, signal, sender, slot, Qt::DirectConnection);invalid use of incomplete type 'struct QtPrivate::QEnableIf<false, QMetaObject::Connection>'
declaration of 'struct QtPrivate::QEnableIf<false, QMetaObject::Connection>'
template <bool B, typename T = void> struct QEnableIf;
@EDIT: Solved
-
bq. There can obviously be a performance penalty, but depending on the size of your data and the frequency of access, it might not be noticable at all…
Well I have currently massive performance problems using signal ans slots.
The CPU usage is > 25% and the animation are played slower than the microcontroller would do. -
I probably know why there is such a huge performance problem, there is anywhere in my Threads a massive memory leak.
When I start the application it needs 14kB memory, when I start the animation Thread and sending Thread. The memory usage climbs over 1GB within 10-30 seconds :-( !
I dont understand it.