Does a thread is essential to draw line at the same time receive data?

gui
#1

I made a component to draw lines according to the data from a device, but the component can’t work if it keeps receiving data at a same time drawing lines.
Do I need create a thread outside the component to receive data? If a thread is essential, how can i start it with :startThread() and where to start it.
I would appreciate it if anyone can give some suggestions.

0 Likes

#2

You don’t need to create an additional thread for painting, in fact it wouldn’t help you. There are already multiple threads running because of this problem. One is the GUI thread, aka non-realtime thread, aka MessageManager, that handles mouse, keyboard, painting etc.
And there is the audio thread, which is realtime and is supplying the audio driver with data.

They must both run independently, that is the audio thread must not have to wait for the non-realtime thread. The other way round is ok, but if it can be avoided, do so.

The solution is a common data structure, that the audio thread (or your device thread, if it is something externally driven) feeds into, and the GUI thread reads from it just the amount of data that it needs to draw. This is called FIFO or ring buffer. There are different versions of it, depending on what you need, e.g. is it enough to display the latest data, or does every packet need to be consumed (for visualisation often not necessary, for finding a max and min it is).

0 Likes

#3

Thank you for your suggestions :smile: , but the program will go wrong when i use looping:
while(true)
{
drawLine();
}
to draw lines according to the data.(I use an static Array to save data from device, and then traversing the array to draw line)
So, is it that i understand it wrong?

0 Likes

#4

how are you going to break out of that loop?

0 Likes

#5

Sorry, it’s my fault. I forgot to set a conditional judgement to make sure if there have data in array first.

0 Likes

#6

Your paint() method is drawing a snapshot. It is essential, that the memory you are going to draw during that call is not accessed by the other thread. You can make sure of that by

  1. using a lock - dangerous, you can stall the audio thread, if used wrong (priority inversion)

  2. having a big buffer and a pointer, which data is ready to be read
    Make sure the buffer is big enough, that when the audio thread wraps around, it is still not writing into the area, that is being painted

Here is an open source example for an analyser I wrote:

This uses indeed an additional thread for calculating the FFT, but the principle is the same, Analyser::addAudioData() is called from the audio thread and it is made sure, that there is always enough space in the FIFO, otherwise it just skips the data, because it must not wait.

Good luck

0 Likes

#7

Note that the poster hasn’t explicitly mentioned anywhere that this is about drawing audio data. Depending on what the poster is attempting to do, launching an additional thread may or may not be needed. Often “device” APIs will have their own thread on which they make their callbacks that provide data, but sometimes that isn’t the case and the client code needs to do its own polling loop or something.

0 Likes

#8

I know, he said “a device”, and I just gave the code for inspiration…

Yes, see my first answer for that.

0 Likes

#9

That kind of pattern does not (directly) work in GUI frameworks like JUCE, you should not do infinite or even other kinds of long lasting loops in the GUI thread. The GUI will not get updated while in the loop nor will user interaction with the mouse or keyboard be possible.

0 Likes

#10

Thank you so much :slight_smile: , I will try it.

0 Likes

#11

Thank you for your advice. I am trying to receive data from a serial device but not for sure how to get and save data to draw lines.

0 Likes

#12

Yes, you are right, i will change this mistake.

0 Likes

#13

Depending on how much work needs to be done when receiving your data from the serial port, you could handle this on the MessageThread too. If you want to do so, create a class inheriting from Timer, acquire the new data from the port in the timer callback and call Component::repaint() on the component you are using to visualize the data. The timer callback will be scheduled to be called roughly at the desired interval on the MessageThread. However you should only go for that solution if acquiring the data is a lightweight task. If it would block longer this would lead to GUI freezing or a not very responsive user interface resulting in a bad user experience.

0 Likes

#14

Can you elaborate a bit about how you are reading from the serial port?
Since that is afaik a hardware interrupt (it’s been a while, that I worked with these kind of low level ports), there must be some kind of asynchronous framework in place…

0 Likes

#15

Ok,
I include a header file in my code like this:


i need to open or connect the port and receive data from it, then put the data to somewhere to draw lines. I originally planed to create an array to save and use data, but now it doesn’t seems a good idea.

0 Likes

#16

Hijacking a bit your thread, do you mind if I ask which aproach did you finally end up going with?

In my case hardware interrupts are managed by an Arduino/Atmel that does all the dirty job reading values from buttons and knobs, and then send the values read as bytes through serialport to JUCE, where I grab them in the GUI thread every 20ms using a timer (which also updates the slider or button value).

Should I fill a FIFO with a polling thread alas @daniel analyzer and grab the values there in the GUI thread to set the sliders? I’m already sending only a few bytes from arduino, so I find it a bit redundant since I’d need another timer in the GUI to grab values which I’m already doing now in my sketch code.

0 Likes