PDA

View Full Version : Serail Port GUI



ShaChris23
17th April 2007, 17:50
Hi people,

I'm an experienced C++ programmer, wanting to do GUI for the first time. I decided to go with Qt 4.2.3 because of its documentation and because it's a commercial product.

I have written a back-end C++ program that reads GPS, and other modules data via multiple serial ports using Posix threads Win32 and a simple CSerial library, and now I would like to display the data read on the GUI.

I have done the Qt tutorial, etc...but I dont feel like the tutorial really preps you for the things I'm doing..does anyone have a sample app that display values that are changed via other I/O not from the GUI? (i.e. data constantly changing because of serial port incoming data not because the user clicks something.)

Since there are multiple threads, I would think there needs to be a mutex around the displayed data ...doesnt it?

Any help would be greatly appreciated. I actually have more questions but I guess I will just start off like this.

I'm really excited to be able to do GUI with Qt. Hopefully, in the future I would be able to learn other aspects of Qt as well.

high_flyer
17th April 2007, 18:10
Welcome to Qt! :)
Since you are not trying to do any I/O with Qt, there is no problem what so ever for you.
There are many ways do to what you are asking.
Try to be more specific with your questions -> in regard to what it is you do not understand, and show what you already tried.
This will help us help you better.

fullmetalcoder
17th April 2007, 18:18
it's a commercial product.
Not only and that's probably on of its major advantages... :) But I guess that's not really the point here...:rolleyes:


I have written a back-end C++ program that reads GPS, and other modules data via multiple serial ports using Posix threads Win32 and a simple CSerial library, and now I would like to display the data read on the GUI.
If you want to use Qt is is highly recommended, though not mandatory, to rely on QThread to handle threaded operations. However this remarks only stands if you want to embed your existing program in a GUI and not to provide a graphical front-end communicating with the back end through a CLI.


display values that are changed via other I/O not from the GUI? (i.e. data constantly changing because of serial port incoming data not because the user clicks something.)
This is not a problem... All you have to do is understand OOP, signals and slots and Qt event loop. Then think carefully about the data flow design and everything will be alright...:)


Since there are multiple threads, I would think there needs to be a mutex around the displayed data ...doesn't it? It depends on your design... Since the "displayed" data will be owned by the GUI thread (through widgets) there shouldn't be much troubles if data is sent somehow sequentially by working threads through signals/slots mechanism. However if a data structure is shared using, for instance, producer/consumer paradigm, you will probably need to play with QSemaphore, QMutex, ...

ShaChris23
17th April 2007, 18:26
Hi,

Here's a snippet from one of my threads. This thread reads a 1Hz GPS data via a serial port. The code below shows code mixed with comments to improve readability, and ignore un-needed technicality.



// Open a serial port

while( mRunningFlag )
{
// Blocking-Read NMEA message

// Now I have some values I would like to display to the GUI.
// For example, these floating-point numbers were updated from the previous read and
// I would like to display it onto the screen
double lat;
double lon;
float utcTime;
}


What widget/class/function calls should I use to display those values? I think I can do the layout myself since the tutorial #1 taught me about that.

ShaChris23
17th April 2007, 18:33
This is not a problem... All you have to do is understand OOP, signals and slots and Qt event loop. Then think carefully about the data flow design and everything will be alright...:) ...



Hi, yes, I understand signals and slots quite well, though I probably cant use them to their fullest potential yet since I'm new to this. As for the event loop, I'm just learning about it.




It depends on your design... Since the "displayed" data will be owned by the GUI thread (through widgets) there shouldn't be much troubles if data is sent somehow sequentially by working threads through signals/slots mechanism. However if a data structure is shared using, for instance, producer/consumer paradigm, you will probably need to play with QSemaphore, QMutex,



Since my back-end program was written in straight C++ in a producer/consumer paradigm, I already had mutex to protect the shared resource. So I guess my GUI thread will need to use mutex as well. Can you pass a data structure with signal/slots (is there a shorthand/abbreviation for this (like SS)? From the examples, I have only seen passing of integers. Though I try to avoid passing data around due to performance reason...

marcel
17th April 2007, 18:38
What widget/class/function calls should I use to display those values? I think I can do the layout myself since the tutorial #1 taught me about that.

:)
You could use a QTableWidget with three columns: lat, lon and utc time.

You can transmit these values to the interface via signals.
You emit one signal from the thread :



emit ( lat, lon, utc );


You receive them in a slot in the interface, and display them.

The problem remains with the interface responsiveness while it receives this data ( I assume you get a large number of data sets from the serial port ). The signal will be posted as an event to the interface thread and posting too many events in a short period of time can slow the interface down.

I recommend that you buffer the data in the thread, let's say about 100 data sets, and you emit the whole buffer at once. You can pass a list to the signal, or whatever container you like. This way the table widget will update rarely, with 100 sets at the time.

What do you think?

Regards

marcel
17th April 2007, 18:46
Can you pass a data structure with signal/slots (is there a shorthand/abbreviation for this (like SS)? From the examples, I have only seen passing of integers. Though I try to avoid passing data around due to performance reason...
You have to register your custom type as a meta type to be able to pass it to signals/slots.
See qRegisterMetaType (http://doc.trolltech.com/latest/qregistermetatype.html) and Q_DECLARE_METATYPE (http://doc.trolltech.com/latest/q_declare_metatype.html) for more description(in the Assistant).

Especially:


Note that if you intend to use the type in queued signal and slot connections, you also have to call qRegisterMetaType (http://www.qtcentre.org/forum/qmetatype.html#qRegisterMetaType)() since such connections are resolved at runtime.

regards

ShaChris23
17th April 2007, 18:52
Hi Marcel,

Thanks for the suggestion. Let me show the code that you suggested, and tell me if I misunderstood, or I'm doing anything wrong.





// GUI Thread
class GpsGui : public QTableWidget
{

public slots:
// Display data using QTableWidget
void displayData( double, double, double );
};

// GPS Read thread
double lat, lon, utc;
while( 1 )
{
// Read GPS data & modify lat, lon, utc

// send the data
emit( lat, lon, utc );
}


Is that code skeleton kind of right (both syntax and semantic)? But where should I do the connect( ) to connect the GPS Read thread's signal to the GpsGui's slot?

Note GPS Read thread and GpsGui are in different files.

Sorry if the code is just wrong..I'm trying to learn this.

marcel
17th April 2007, 19:01
ALSO: You need to define a signal that you emit in the thread, like:


//in thread definition:
signals:
void values( double, double, double );
Then you can do in the while loop:


emit values( val1, val2, val3 );
Where do you create the thread? Is it in the Gui?
If so, try this:



connect( workerThread, SIGNAL( values( double, double, double ) ), this, SLOT( displayValues( double, double, double ) ) );
If it is not, then I suggest implementing the singleton pattern for your main window. Doing this you'll provide a GPSGui::Get() static method.

You'll be able to do( before actually starting the thread):

connect( workerThread, SIGNAL( values( double, double, double ) ), GPSGui->Get(), SLOT( displayValues( double, double, double ) ) );Having said this, I assumed your thread is a QThread... Is it? Because if it isn't, you'll need to approach things differently...

regards

fullmetalcoder
17th April 2007, 19:02
What widget/class/function calls should I use to display those values? I think I can do the layout myself since the tutorial #1 taught me about that.
It depends on what you want to achieve... marcel suggested a QTableWidget which would be fine in case you want to display kinda log for these three values. However if all you need is these three values to be shown as they are at a given instant then just create QLabel (http://doc.trolltech.com/latest/qlabel.html) to display the latitude and longitude (also check QString::number(double) (http://doc.trolltech.com/4.2/qstring.html#number-7) to achieve proper display). The time display will probably a little trickier but nothing impossible... QDate, QDateEdit (http://doc.trolltech.com/latest/qdateedit.html), QTime, QTimeEdit (http://doc.trolltech.com/latest/qtimeedit.html), QDateTime and QDateTimeEdit (http://doc.trolltech.com/latest/qdatetimeedit.html) should help you to deal with this.


Since my back-end program was written in straight C++ in a producer/consumer paradigm, I already had mutex to protect the shared resource. So I guess my GUI thread will need to use mutex as well. Can you pass a data structure with signal/slots (is there a shorthand/abbreviation for this (like SS)? From the examples, I have only seen passing of integers. Though I try to avoid passing data around due to performance reason...
Actually you may not need to mess with QMutex and other Qt threading classes... All you'll need is a class inheriting from QObject which will emit proper signal anytime one of your three values changes. If I understand your design well enough this class should be the class in which the snippet you showed us is defined. Just add members to it :


class myClass : public QObject //, ...
{
//...

signals:
void timeChanged(double latitude);
void latitudeChanged(double latitude);
void longitudeChanged(double latitude);

};Then connect these signals to proper slots in the widgets you created (possible changes may be needed for date/time display...) and place the following code in your loop :



// when latitude changes :
emit latitudeChanged(latitude);

// when longitude changes :
emit longitudeChanged(longitude);

// when time changes :
emit timeChanged(time);
Hope this helps. :)

ShaChris23
17th April 2007, 19:15
Hi fullmetalcoder,

Yes, you understood what I wanted to do right. Now that those signals are emitted, then on the GUI side, I can do the following right?




// First, somewhere before the threads are started, I do (say..just for latitude for now)
connect( myClass, SIGNAL(latitiudeChanged(double)), MyGui, SLOT(displayLat) );

// Then here's the implementation of MyGUI
class MyGui : public QLabel
{
public slots:
void displayLat( double lat );
};


What would the function definition of void displayLat( double lat ) be like so that I can display the current latitude?

marcel
17th April 2007, 19:22
OK, I've read what you posted before and I concluded your threads are not QThreads. Because of this you cannot use the signals/slots mechanism.
In this case you'll have to use straight events :). This will make it easier passing custom types.

In the GUI


bool GPSGui::event( QEvent* e )
{
if( e->type() == QEvent::User )
{
CMyEvent *me = dynamic_cast<CMyEvent*>(e);
if( me )
{
updateTableWidget( me->dataBuffer );
delete me->dataBuffer;
me->dataBuffer = NULL;
e->accept();
return true;
}
}
return QMainWindow::event(e);
}


In the thread:



float v1, v2, v3;
while(1)
{
....
//Fill the buffer with data sets
QApplication::postEvent( GPSGui::Get(), new CMyEvent( dataBuffer ) );
}


If you agree, then I'll show you how to implement the event, which is not really hard.
Note that dataBuffer (in the thread ) must be allocated on the heap and deleted in the GUI.

Regards

ShaChris23
17th April 2007, 19:24
Hi Marcel...

I create all the the threads in my main(). Perhaps Qt's thread instantiation is done in a different fashion, and I havent looked into it yet. I've been focusing on just the GUI part...

I'm using Posix threads...
Right now, my program is kinda like..



int main()
{
// Initialize all the data classes

// Create threads...
}

void* threadFunc1(void* param)
{
while(1)
// ...

}

void* threadFunc2(void* param)
{
while(1)
// ...
}

marcel
17th April 2007, 19:28
Yes, and this is why you have to use events/event handling instead of signals/slots...Which is pretty much the same in this case, just more to code...

Unless you want to make your threads QThreads...
You must understand that only classes derived from QObjects ( in this case must be QThread ) can emit signals ( or receive, but to receive you will also need to start an event loop in your thread with exec() );


After all, it is not that hard what you're trying to achieve... Shouldn't take more than 2-3 hours to get something working...

Regards

ShaChris23
17th April 2007, 19:36
Hi Marcel,

I dont fully understand the event code that you just implemented...however, please do show how to implement the event if you would like just so I will get a better understanding.

I'm also going to look up on QThread example...maybe it's worth it to just convert my design to QThreads.

ShaChris23
17th April 2007, 19:46
Hi Marcel..

The QThread is very similar to Java's ..so I dont think it's going to be a problem, I can certainly use it. Sorry to keep switching back and forth.

marcel
17th April 2007, 19:47
Sure...
If you decide to convert to QThreads then you won't need the custom events anymore. But for the sake of completeness... :)

Declaration:


#include <QEvent>

class CMyEvent : public QEvent
{
Q_OBJECT

public:
CMyEvent( BufferStructure* );
~CMyEvent();

BufferStructure* dataSets;
}


Definition:


#define EVENT_TYPE (QEvent::User)
CMyEvent::CMyEvent( BufferStructure* dSets )
: QEvent( EVENT_TYPE )
{
dataSets = new BUfferStructure( *dSets );
}

CMyEvent::~CMyEvent()
{
delete dataSets;
}


Given the fact that dataSets is now deleted in the destructor, it shouldn't be deleted in the event of GPSGui, as I did it earlier.

So, this is it.

Regards.

marcel
17th April 2007, 19:49
The QThread is very similar to Java's ..so I dont think it's going to be a problem, I can certainly use it. Sorry to keep switching back and forth.


No problem...
I guess you can now just use signals, and lose the custom events...