Page 1 of 3 123 LastLast
Results 1 to 20 of 47

Thread: QTcpSocket readyRead strange behavior

  1. #1
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default QTcpSocket readyRead strange behavior

    hi,

    i've made a simple client/server example :

    * The client connects to the server
    * the server regulary sends data :
    ** size of buffer (short)
    ** buffer index (unsigned long)
    ** buffer, filled with zeros
    * the client receives this data
    ** reads size
    ** reads buffer
    ** prints index.

    Packet size should be always the same (19998).
    But randomly, the client doesn't read the size, but reads 0. It seems there was a problem while reading the stream.

    here's the log of my application :
    ...
    onSocketReadyRead bytes= 7504
    Bytes= 7504
    Packet size= 19998
    not enough data in stream
    onSocketReadyRead bytes= 15008
    Bytes= 15008
    Packet size= 19998
    not enough data in stream
    onSocketReadyRead bytes= 20000
    Bytes= 20000
    Packet size= 19998
    data read, packet index= 6733
    onSocketReadyRead bytes= 0
    onSocketReadyRead bytes= 3752
    Bytes= 3752
    Packet size= 0 <-- ERROR!

    here's the code of the QThread client :
    Qt Code:
    1. Client::Client(QObject *parent) : QThread(parent)
    2. {
    3.  
    4. }
    5.  
    6. Client::~Client()
    7. {
    8.  
    9. }
    10.  
    11. void Client::run()
    12. {
    13. pSocket = new QTcpSocket(this);
    14.  
    15. connect(pSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
    16.  
    17. pSocket->connectToHost("192.168.50.77", 1234);
    18. if (!pSocket->waitForConnected())
    19. {
    20. qDebug()<<"Unable to connect";
    21. return;
    22. }
    23.  
    24. exec();
    25. }
    26.  
    27. void Client::onSocketReadyRead()
    28. {
    29. short sSize;
    30. char* data;
    31. unsigned long lPacketIndex;
    32. qint64 nBytes = pSocket->bytesAvailable();
    33.  
    34. qDebug()<<"onSocketReadyRead bytes="<<nBytes;
    35.  
    36. if (nBytes < 2)
    37. return;
    38. qDebug()<<"Bytes="<<nBytes;
    39. pSocket->peek((char*)&sSize, 2);
    40.  
    41. qDebug()<<"Packet size="<<sSize;
    42. if (sSize != 19998)
    43. {
    44. QMessageBox::critical(NULL, tr("error"), tr("This is it"));
    45. pSocket->disconnectFromHost();
    46. return;
    47. }
    48.  
    49. if (nBytes < 2 + sSize)
    50. {
    51. qDebug()<<" not enough data in stream";
    52. return;
    53. }
    54.  
    55. data = new char[sSize];
    56.  
    57. pSocket->read((char*)&sSize, 2);
    58. pSocket->read(data, sSize);
    59.  
    60. memcpy((char*)&lPacketIndex, data, 4);
    61.  
    62. qDebug()<<" data read, packet index="<<lPacketIndex;
    63.  
    64. delete data;
    65. }
    To copy to clipboard, switch view to plain text mode 

    What do i do that i should not do?
    Is it a Qt bug?
    This problem is random but can be reproduced more easily with a corrupted network (bad wifi connection, etc...)
    it MAY be caused by TCP packet retransmission on timeout (i've seen such retransmission with wireshark)

    thanks

  2. #2
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket readyRead strange behavior

    Packet size should be always the same (19998).
    I don't think you have control over the size of the underlying packet. (the ones the server or any servers in between are packing).
    What you can do is wait in onSocketReadRead() until the at least 19998 bytes are available before you read.
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  3. #3
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket readyRead strange behavior

    Quote Originally Posted by naroin View Post
    What do i do that i should not do?
    You assume that if some data arrives then all data arrives which is not the case. TCP is a stream of bytes without any internal structure. The fact that the sender sends 5 different chunks of data doesn't mean it will also arrive in 5 chunks. It may be one chunk or 99999+ chunks.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  4. #4
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    thats what i do.

    i ready 2 bytes, that should contain the packet size (here always 19998), then i read the buffer.
    As a consequence, i should always read 19998 as the size, but i dont.

  5. #5
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket readyRead strange behavior

    Quote Originally Posted by naroin View Post
    i ready 2 bytes, that should contain the packet size (here always 19998), then i read the buffer.
    "packet" is the term reserved for Internel Protocol (IP). TCP works in a concept of "segments". So 19998 is definitely not the packet size. You are sending a stream of 19998 bytes, that's it. You never know across how many packets or segments the data spans.

    As a consequence, i should always read 19998 as the size, but i dont.
    No, you shouldn't. Eventually you will get 20000 (2+19998) bytes at the receiving side but there is no guarantee they will arrive at the same time.
    Last edited by wysota; 18th January 2011 at 14:24.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  6. #6
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    ok, sorry, my terms are not very clear
    19998 is my DATA size.
    i'm sending a stream of 20000 bytes, the first 2 containing the size of the rest of the data (that is, 19998).

    i know my data won't arrive at the same time, and if you look at the code, i verify that i have enough data before reading it.
    If i do it wrong, i really don't know how i can do right.

    What should i change in my code to make it works?

  7. #7
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket readyRead strange behavior

    What should i change in my code to make it works?
    Do what I answered you in my previous post!
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  8. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket readyRead strange behavior

    Well, I would definitely not cast char* to short like that. It is possible the sending side doesn't send it the way you expect it and you get bogus data.

    Try something like:
    Qt Code:
    1. QByteArray buffer; // somewhere
    2. void X::onReadyRead() {
    3. buffer += socket->readAll(); // read all pending data
    4.  
    5. while(buffer.size()>=2){
    6. quint16 size = (buffer.at(0) << 8) + buffer.at(1); // 16bit unsigned value
    7. if(buffer.size() <= 2+size) return;
    8. QByteArray data = buffer.mid(2, size);
    9. processData(data);
    10. buffer.remove(0,2+size);
    11. }
    12. }
    To copy to clipboard, switch view to plain text mode 

    Of course adjust the sending side to send the size in the same manner.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  9. #9
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    high_flyer i really dont understand, i'm sorry if i am stupid, i DONT ASSUME that all data arrives if SOME data arrives.
    Check the code:
    Qt Code:
    1. qint64 nBytes = pSocket->bytesAvailable();
    2.  
    3. if (nBytes < 2)
    4. return;
    5. pSocket->peek((char*)&sSize, 2);
    6.  
    7. if (nBytes < 2 + sSize)
    8. {
    9. qDebug()<<" not enough data in stream";
    10. return;
    11. }
    To copy to clipboard, switch view to plain text mode 


    Added after 32 minutes:


    wysota, i've got exactly the same problem.

    Sometimes, i read a size of 0 instead of 19998.
    i've changed the content of my data
    memset(data, 1, 19998);
    then the size i read is 257. (0x0101)

    as a consequence, there seems to be a shift in stream. Can it be due to TCP retransmission on error?
    Windows socket bug? Qt bug?

    thanks for your help.
    Last edited by naroin; 18th January 2011 at 15:14.

  10. #10
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    no more help?

  11. #11
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket readyRead strange behavior

    I don't understand what help you want.
    Can you show your current code?
    Do you read until you get the buffer in the size you expect?
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  12. #12
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    yes i read until i get the buffer in the size i expect

    the code is in my first post... where is it false??

    void Client:nSocketReadyRead()

  13. #13
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket readyRead strange behavior

    Your code in the first post does NOT wait for the expected size:
    Qt Code:
    1. qint64 nBytes = pSocket->bytesAvailable(); //you check bytes available
    2.  
    3. qDebug()<<"onSocketReadyRead bytes="<<nBytes;
    4.  
    5. if (nBytes < 2) // it could be you get 0 or 1 bytes, so you get out here. But lets say you got 3, so we go on.
    6. return;
    7. qDebug()<<"Bytes="<<nBytes;
    8. pSocket->peek((char*)&sSize, 2);//why do you use peek and not bytesAvailable? also, why do you peel only 2 bytes ahead? it will alway be at most 2 bytes this the following if will always get out.
    9.  
    10. qDebug()<<"Packet size="<<sSize;
    11.  
    12. if (sSize != 19998) //so we didn't read 19998, (sSize will be at most 2 because of the peek) which is the size you are waiting for, so we get in to the if.
    13. {
    14. QMessageBox::critical(NULL, tr("error"), tr("This is it"));
    15. pSocket->disconnectFromHost();
    16. return; //And what do you do? you get out!!
    17. }
    18.  
    19. if (nBytes < 2 + sSize)
    20. {
    21. qDebug()<<" not enough data in stream";
    22. return;
    23. }
    To copy to clipboard, switch view to plain text mode 

    EDIT:
    Qt Code:
    1. if (sSize != 19998)
    To copy to clipboard, switch view to plain text mode 
    is a tottaly a problem.
    Read the peek documentation and be sure to understand it.
    sSize should be a buffer, as long as the the data you want to read.
    It does not store the size read size, but the read data!
    If at all, you probably then use it like this:
    Qt Code:
    1. int iSize = peel(buff,maySize);
    2.  
    3. if(iSize != iExpectedSize){...}
    To copy to clipboard, switch view to plain text mode 
    Last edited by high_flyer; 19th January 2011 at 10:11.
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  14. #14
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    the buffer i want to stream contains:
    2 bytes, containing the size of the buffer
    rest of the buffer.
    in the final program, the size may differ from one packet to another, so i have to put it at the beginning of the send data.
    For my tests here, i put it at 19998.

    the goal is to read
    * the size (in the 2 first bytes)
    * then the buffer, corresponding to size just read
    then redo the same.

    here's my code with comments:

    Qt Code:
    1. void Client::onSocketReadyRead()
    2. {
    3. short sSize;
    4. char* data;
    5. unsigned long lPacketIndex;
    6. qint64 nBytes = pSocket->bytesAvailable();
    7.  
    8. qDebug()<<"onSocketReadyRead bytes="<<nBytes;
    9.  
    10. if (nBytes < 2) //we haven't read enough data to get the buffer size, return
    11. return;
    12. qDebug()<<"Bytes="<<nBytes;
    13. pSocket->peek((char*)&sSize, 2); //we read 2 bytes, we get the size of the buffer INSIDE these 2 bytes
    14. //here i use peek to get the 2 bytes containing the size, without removing it from the buffer, because i may have not enough data, and i'll have to redo it.
    15.  
    16. qDebug()<<"Packet size="<<sSize;
    17. /* this IF is only to get easily the error . It's DEBUG code.
    18.   as i KNOW the size must be 19998, if it's not, there was an error.
    19. */
    20. if (sSize != 19998)
    21. {
    22. //i put a breakpoint here, the code stops.
    23. QMessageBox::critical(NULL, tr("error"), tr("This is it"));
    24. pSocket->disconnectFromHost();
    25. return;
    26. }
    27.  
    28. if (nBytes < 2 + sSize) //here we wait to have enough data to read sSize bytes
    29. {
    30. qDebug()<<" not enough data in stream";
    31. return;
    32. }
    33.  
    34. data = new char[sSize];
    35.  
    36. pSocket->read((char*)&sSize, 2);
    37. pSocket->read(data, sSize);
    38.  
    39. memcpy((char*)&lPacketIndex, data, 4);
    40.  
    41. qDebug()<<" data read, packet index="<<lPacketIndex;
    42.  
    43. delete data;
    44. }
    To copy to clipboard, switch view to plain text mode 

    is it clearer?

  15. #15
    Join Date
    Jan 2006
    Location
    Munich, Germany
    Posts
    4,714
    Thanks
    21
    Thanked 418 Times in 411 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: QTcpSocket readyRead strange behavior

    wysota gave you the solution, why don't you use it?
    ==========================signature=============== ==================
    S.O.L.I.D principles (use them!):
    https://en.wikipedia.org/wiki/SOLID_...iented_design)

    Do you write clean code? - if you are TDD'ing then maybe, if not, your not writing clean code.

  16. #16
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    I have answrered : i've exactly the same problem with wysota's solution.

    As i already said, it works well for a certain time, then it randomly fail. In addition, it does not fail in a "good" network with no TCP retransmission, and often fails in a "bad" network, like low wifi.

    well, i'll use directly windows sockets.
    Last edited by naroin; 19th January 2011 at 11:10.

  17. #17
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket readyRead strange behavior

    Can we see the code for the sender? Also are the sender and the receiver running the same operating system and the same CPU type?
    Last edited by wysota; 19th January 2011 at 11:37.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  18. #18
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    yes, they are both on windows,

    here's the server side code :
    Qt Code:
    1. #define DATA_SIZE 20000
    2.  
    3. Server::Server(QObject *parent) : QThread(parent)
    4. {
    5.  
    6. }
    7.  
    8. Server::~Server()
    9. {
    10.  
    11. }
    12.  
    13. void Server::run()
    14. {
    15. pServer = new QTcpServer();
    16. pMapperReadyRead = new QSignalMapper();
    17. pMapperDisconnected = new QSignalMapper();
    18. pMapperError = new QSignalMapper();
    19.  
    20. connect(pServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()), Qt::DirectConnection);
    21. connect(pMapperReadyRead, SIGNAL(mapped(QObject*)), this, SLOT(onSocketReadyRead(QObject*)), Qt::DirectConnection);
    22. connect(pMapperDisconnected, SIGNAL(mapped(QObject*)), this, SLOT(onSocketDisconnected(QObject*)), Qt::DirectConnection);
    23.  
    24. pServer->listen(QHostAddress::Any, 1234);
    25.  
    26. //timer d'envoi
    27. lPacketIndex = 0;
    28. QTimer* pTimer = new QTimer(this);
    29. connect(pTimer, SIGNAL(timeout()), this, SLOT(onTimeout()));
    30.  
    31. pTimer->setSingleShot(false);
    32. pTimer->start(100);
    33.  
    34.  
    35.  
    36. exec();
    37.  
    38. pServer->close();
    39. delete pServer;
    40.  
    41. delete pMapperReadyRead;
    42. delete pMapperDisconnected;
    43. delete pMapperError;
    44. }
    45.  
    46. void Server::onNewConnection()
    47. {
    48. QTcpSocket* pSocket = pServer->nextPendingConnection();
    49. if (pSocket == NULL)
    50. return;
    51.  
    52. qDebug()<<"new connection";
    53.  
    54. vecClient.push_back(pSocket);
    55.  
    56. connect(pSocket, SIGNAL(readyRead()), pMapperReadyRead, SLOT(map()), Qt::DirectConnection);
    57. pMapperReadyRead->setMapping(pSocket, pSocket);
    58. connect(pSocket, SIGNAL(disconnected()), pMapperDisconnected, SLOT(map()), Qt::DirectConnection);
    59. pMapperDisconnected->setMapping(pSocket, pSocket);
    60. }
    61.  
    62. void Server::onSocketReadyRead( QObject* pObject )
    63. {
    64.  
    65. }
    66.  
    67. void Server::onSocketDisconnected( QObject* pObject )
    68. {
    69. int i;
    70. QTcpSocket* pSocket = (QTcpSocket*)pObject;
    71.  
    72. qDebug()<<"Socket disconnected";
    73.  
    74. for (i=0;i<vecClient.size();++i)
    75. {
    76. if (pSocket = vecClient[i])
    77. {
    78. vecClient.erase(vecClient.begin() + i);
    79. break;
    80. }
    81. }
    82.  
    83. pSocket->deleteLater();
    84. }
    85.  
    86. void Server::onSocketError( QObject* pObject, QAbstractSocket::SocketError error )
    87. {
    88. QTcpSocket* pSocket = (QTcpSocket*)pObject;
    89.  
    90. qDebug()<<"Socket error : "<<pSocket->errorString();
    91. }
    92.  
    93. void Server::onTimeout()
    94. {
    95. int i;
    96. char data[DATA_SIZE];
    97. short sSize = DATA_SIZE - 2;
    98.  
    99.  
    100. memset(data, 0, DATA_SIZE);
    101. memcpy(data, &sSize, 2);
    102. memcpy(data+2, &lPacketIndex, 4);
    103.  
    104. ++lPacketIndex;
    105.  
    106. for (i=0;i<vecClient.size();++i)
    107. {
    108. vecClient[i]->write(data, DATA_SIZE);
    109.  
    110. }
    111. }
    To copy to clipboard, switch view to plain text mode 

    i can also send you VC++ projects, if you need them

  19. #19
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTcpSocket readyRead strange behavior

    This code is not compliant with what I have shown you. You have not implemented what I suggested so do that first and then we can continue.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  20. #20
    Join Date
    Nov 2010
    Posts
    28
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTcpSocket readyRead strange behavior

    yeah, that's my server.

    the client connects to it, then the server regulary sends data (onTimeout)
    In my project, it's the server that pushes bytes in the socket.

    as you can see line 108 :
    Qt Code:
    1. vecClient[i]->write(data, DATA_SIZE);
    To copy to clipboard, switch view to plain text mode 

Similar Threads

  1. QtcpSocket readyRead Problem with Java Client
    By Bernie in forum Qt Programming
    Replies: 6
    Last Post: 12th February 2011, 12:59
  2. QTcpSocket and readyRead and QTimer
    By cafu in forum Qt Programming
    Replies: 2
    Last Post: 18th December 2009, 14:36
  3. Question in readyRead(QTCPSOCKET)
    By morgana in forum Qt Programming
    Replies: 2
    Last Post: 24th July 2008, 19:11
  4. QTcpSocket readyRead and buffer size
    By pdoria in forum Qt Programming
    Replies: 4
    Last Post: 2nd February 2008, 11:11
  5. Strange QTcpSocket behavior (debugged with Ethereal)
    By mdecandia in forum Qt Programming
    Replies: 23
    Last Post: 28th May 2007, 21:44

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.