Results 1 to 3 of 3

Thread: File Transfer with QTcpServer and QTcpSocket

  1. #1
    Join Date
    Apr 2008
    Posts
    196
    Thanked 8 Times in 6 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows
    Wiki edits
    1

    Default File Transfer with QTcpServer and QTcpSocket

    Hey @all,

    now comes my first post ;-) This forum is really great.

    My Problem is to Transfer large binary files over the network >= 90 GB. So i will do this as fast as possible.

    My client and server are clones from the fortune client and threaded fortune server.
    Can anybody explain me or give me a hint what's wrong. Because when i transfer a file from windows to windows the server prints "readClient();" but the client is already finished. And on the other situation when i transfer from Windows to Linux, the client dies with the message: "QWaitCondition: Destroyed while threads are still waiting" and the server wrote something with "Can't load type 4321342 from QVariant".

    But here is the Client code and in the next posting is the server-thread code:
    main.cpp
    Qt Code:
    1. #include <QtCore/QCoreApplication>
    2. #include "client.h"
    3. #include <iostream>
    4.  
    5. using namespace std;
    6.  
    7. int main(int argc, char *argv[]) {
    8. QCoreApplication a(argc, argv);
    9.  
    10. Client client(a.argv()[1], a.argv()[2]);
    11. client.transfer_file(QString("%1").arg(a.argv()[3]).toInt(), QString("%1").arg(a.argv()[4]).toInt(),
    12. a.argv()[5], a.argv()[6]);
    13.  
    14. return a.exec();
    15. }
    To copy to clipboard, switch view to plain text mode 
    client.h
    Qt Code:
    1. #ifndef CLIENT_H
    2. #define CLIENT_H
    3.  
    4. #include <QObject>
    5. #include <QTcpSocket>
    6. #include <QSslSocket>
    7. #include <QSslError>
    8. #include <QFile>
    9. #include <QFileInfo>
    10. #include <iostream>
    11. #include <windows.h>
    12.  
    13. class Client : public QObject {
    14. Q_OBJECT
    15.  
    16. public:
    17. Client(QString hostname, QString port);//(QObject *parent);
    18. ~Client();
    19. void setDir(QString _directory);
    20.  
    21. private slots:
    22. void onStateChanged(QAbstractSocket::SocketState socketState);
    23. void onMessage(const QString &msg, const QVariant &data);
    24. void onConnected();
    25. void onDisconnected();
    26. void sendMessage(const QString &msg, const QVariant &data);
    27. void onReadyRead();
    28. void displayError(QAbstractSocket::SocketError socketError);
    29. /*void socketEncrypted();
    30. void sslErrors(const QList<QSslError> &errors);
    31. void onEncrypted();*/
    32.  
    33. public slots:
    34. void transfer_file(qint64 _bytes_to_read, qint64 _to_sleep = 50, QString filename = "", QString remote_filename = "");
    35. //void transfer_file(QString filename);
    36.  
    37. private:
    38. double tick_diff;
    39. qint64 max_bytes_for_all;
    40.  
    41. QString _directory;
    42. QTcpSocket *tcpSocket;
    43. //QSslSocket *tcpSSLSocket;
    44. QString currentFortune;
    45. quint32 blockSize;
    46. bool reading_message;
    47. qint64 percentage(qint64 max, qint64 work);
    48.  
    49. };
    50.  
    51. #endif // CLIENT_H
    To copy to clipboard, switch view to plain text mode 
    client.cpp
    Qt Code:
    1. #include "client.h"
    2. #include <QVariant>
    3. #include <iostream>
    4. #include <QDir>
    5. #include <QSslError>
    6. #include <QSslSocket>
    7. #include <QSslKey>
    8. #include <QSslCipher>
    9.  
    10. #include "messages.h"
    11.  
    12. using namespace std;
    13.  
    14. #define BUFFER_SIZE 2*1024*1024 // 2 MB 16384 // 16 kb
    15. #define TRANSFER_SIZE 16384*3 //2*1024*1024 // 2 MB 16384 // 16 kb
    16. #define TEST_LOOP 1000000
    17. LARGE_INTEGER start_ticks, ende_ticks, frequenz;
    18.  
    19. Client::Client(QString hostname, QString port) {//(QObject *parent) : QObject(parent) {
    20. tcpSocket = new QTcpSocket(this);
    21.  
    22. connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    23. connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
    24. connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    25. connect(tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
    26. connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));
    27.  
    28. blockSize = 0;
    29. tick_diff = 0;
    30. max_bytes_for_all = 0;
    31. blockSize = 0;
    32. tcpSocket->abort();
    33. tcpSocket->connectToHost(hostname, port.toInt());
    34. const int Timeout = 5 * 1000;
    35. if (!tcpSocket->waitForConnected(Timeout)) {
    36. emit displayError(tcpSocket->error());//, tcpSocket->errorString());
    37. //QMessageBox::critical(this, "SOCKET ERROR", tcpSocket->errorString());
    38. return;
    39. }
    40. }
    41.  
    42. Client::~Client() {
    43. }
    44.  
    45. void Client::onStateChanged(QAbstractSocket::SocketState socketState) {
    46. }
    47.  
    48. void Client::setDir(QString _directory) {
    49. this->_directory = _directory;
    50. }
    51.  
    52. void Client::onMessage( const QString &msg, const QVariant &data ) {
    53. if(msg == GD_RETR_WELCOME_STRING) {
    54. if(data.type() == QVariant::Map) {
    55. QMap<QString, QVariant> val;
    56. val = data.toMap();
    57. for(QMap<QString, QVariant>::iterator it = val.begin();
    58. it != val.end(); ++it) {
    59. qDebug() << it.key() << ": " << it.value() << endl;
    60. }
    61. }
    62. }
    63. else {
    64. if(data.type() == QVariant::Map) {
    65. QMap<QString, QVariant> val;
    66. val = data.toMap();
    67. for(QMap<QString, QVariant>::iterator it = val.begin();
    68. it != val.end(); ++it) {
    69. qDebug() << it.key() << ": " << it.value() << endl;
    70. }
    71. }
    72. }
    73. }
    74.  
    75. void Client::onConnected() {
    76. }
    77.  
    78. void Client::onDisconnected() {
    79. if(tcpSocket == NULL)
    80. return;
    81.  
    82. tcpSocket->abort();
    83. }
    84.  
    85. void Client::sendMessage(const QString &msg, const QVariant &data) {
    86. if(tcpSocket == NULL) return;
    87. if(tcpSocket->state() != QAbstractSocket::ConnectedState ) return;
    88.  
    89. QByteArray block;
    90. QDataStream out(&block, QIODevice::WriteOnly);
    91. out.setVersion(QDataStream::Qt_4_2);
    92. //out.setVersion( out.version() ); // set to the current Qt version
    93. out << (quint32) 0;
    94. //out << (quint16) 0;
    95. out << msg;
    96. out << data;
    97. out.device()->seek(0);
    98. out << (quint32)(block.size() - sizeof(quint32));
    99. //out << (quint16)(block.size() - sizeof(quint16));
    100.  
    101. tcpSocket->write(block);
    102. tcpSocket->flush();
    103. }
    104.  
    105. void Client::onReadyRead() {
    106. if (tcpSocket == NULL) return;
    107. if (tcpSocket->state() != QAbstractSocket::ConnectedState) return;
    108.  
    109. QDataStream in(tcpSocket);
    110. in.setVersion(QDataStream::Qt_4_2);
    111. //in.setVersion( in.version() ); // set to the current Qt version instead
    112.  
    113. if(blockSize == 0) {
    114. if (tcpSocket->bytesAvailable() < (int)sizeof(quint32)) return;
    115. //if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return;
    116. in >> blockSize;
    117. reading_message = true;
    118. }
    119.  
    120. if(tcpSocket->bytesAvailable() < blockSize) return;
    121. QString msgString;
    122. QVariant msgData;
    123. in >> msgString;
    124. in >> msgData;
    125.  
    126. emit onMessage(msgString, msgData);
    127.  
    128. reading_message = false;
    129. blockSize = 0;
    130.  
    131. if(tcpSocket->bytesAvailable() > 0) onReadyRead();
    132. }
    133.  
    134. void Client::displayError(QAbstractSocket::SocketError socketError) {
    135. switch (socketError) {
    136. case QAbstractSocket::RemoteHostClosedError:
    137. break;
    138. case QAbstractSocket::HostNotFoundError:
    139. cerr << "The host was not found. Please check the host name and port settings." << endl;
    140. break;
    141. case QAbstractSocket::ConnectionRefusedError:
    142. cerr << "The connection was refused by the peer." << endl;
    143. cerr << "Make sure the fortune server is running, " << endl;
    144. cerr << "and check that the host name and port settings are correct." << endl;
    145. break;
    146. default:
    147. cerr << QString("The following error occurred: %1.").
    148. arg(tcpSocket->errorString()).toStdString() << endl;
    149. }
    150. exit(socketError + 1);
    151. }
    152.  
    153. qint64 Client::percentage(qint64 max, qint64 work) {
    154. return (work*100/max);
    155. }
    156.  
    157. void Client::transfer_file(qint64 _bytes_to_read, qint64 _to_sleep, QString filename, QString remote_filename) {
    158. QFileInfo fi(filename);
    159. qint64 bytes_to_read = _bytes_to_read;//1048576;//2*1024*102416*1024;//1*1024;//16*1024;
    160. qint64 bytes_read = 0;
    161. qint64 max_bytes = fi.size();
    162. qint64 full_max_bytes = max_bytes;
    163. qint64 read_bytes = 0;
    164. qint64 br = 0;
    165.  
    166. cout << "Filesize: " << fi.size() << endl;
    167. cout << "Filesize: " << max_bytes << endl;
    168. cout << "Filesize: " << full_max_bytes << endl;
    169. // return;
    170.  
    171. QFile file(filename);
    172. if(!file.open(QIODevice::ReadOnly))
    173. cout << "Can't read from file '" << filename.toStdString() << "'" << endl;
    174.  
    175. while (max_bytes != 0) {
    176. //while (numberOfPackages > 0){
    177. QMap<QString, QVariant> new_val;
    178. new_val.insert("MD5SUM", "");
    179. new_val.insert("CHUNK", bytes_read);
    180. new_val.insert("FILENAME", filename);
    181. new_val.insert("FILENAME_NEW", remote_filename);
    182. //QCryptographicHash::Md5
    183. QByteArray byteArray;
    184.  
    185. if(bytes_to_read > full_max_bytes)
    186. bytes_to_read = full_max_bytes;
    187.  
    188. read_bytes = bytes_to_read;
    189. if((read_bytes + bytes_read) > full_max_bytes)
    190. read_bytes = full_max_bytes - bytes_to_read;
    191.  
    192. byteArray = file.read(read_bytes);
    193. max_bytes -= byteArray.size();//br;
    194. new_val.insert("SIZE", byteArray.size());
    195.  
    196. if(tcpSocket->state()!=QAbstractSocket::ConnectedState){
    197.  
    198. printf("Socket disconnected error\n");
    199.  
    200. break;
    201. }
    202. if (tcpSocket->bytesToWrite() > 0 && !tcpSocket->waitForBytesWritten(10000)){
    203. printf("Socket write error\n");
    204. printf("socket error: %d %s, socket state: %d\n", tcpSocket->error(), tcpSocket->errorString().toAscii().data(),
    205. tcpSocket->state());
    206. break;
    207. }
    208.  
    209. new_val.insert("CONTENT", byteArray);
    210. //emit sendMessage(GD_FILE, new_val);
    211. sendMessage(GD_FILE, new_val);
    212. bytes_read += byteArray.size();//br;
    213. cout << percentage(full_max_bytes, bytes_read) << " % " << filename.toStdString() << "\r";
    214. cout.flush();
    215. _sleep(_to_sleep);
    216. }
    217. file.close();
    218. }
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Apr 2008
    Posts
    196
    Thanked 8 Times in 6 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows
    Wiki edits
    1

    Default Re: File Transfer with QTcpServer and QTcpSocket

    And here is the Server-Thread code:
    server_thread.h:
    Qt Code:
    1. #ifndef SERVERTHREAD_H
    2. #define SERVERTHREAD_H
    3.  
    4. #include <QThread>
    5. #include <QTcpSocket>
    6. #include <QMutex>
    7.  
    8. class ServerThread : public QThread {
    9. Q_OBJECT
    10.  
    11. public:
    12. ServerThread(int socketDescriptor, QObject *parent = 0);
    13. ~ServerThread();
    14.  
    15. void run();
    16.  
    17. signals:
    18. void error(QTcpSocket::SocketError socketError);
    19.  
    20. private:
    21. QMutex mutex;
    22. bool file_transfer;
    23. struct RemoteHostInformation {
    24. std::string hostname;
    25. std::string address;
    26. int port;
    27. };
    28. struct RemoteHostInformation remote_host_info;
    29. int socketDescriptor;
    30. QTcpSocket *tcpSocket;
    31. //QSslSocket *tcpSSLSocket;
    32. quint32 blockSize;
    33. bool reading_message;
    34.  
    35. private slots:
    36. void sendMessage(const QString &msg, const QVariant &data);
    37. void onReadyRead();
    38. void onMessage(const QString &msg, const QVariant &data);
    39. //void onEncrypted();
    40. //void sslErrors(const QList<QSslError> &err);
    41. void onConnected();
    42. void displayError(QAbstractSocket::SocketError socketError);
    43. void onDisconnected();
    44.  
    45. };
    46.  
    47. #endif // SERVERTHREAD_H
    To copy to clipboard, switch view to plain text mode 
    server_thread.cpp:
    Qt Code:
    1. #include "serverthread.h"
    2. #include <QtNetwork>
    3. #include <iostream>
    4. #include <iomanip>
    5. #include "messages.h"
    6. #include "server.h"
    7. #include <QList>
    8. #include <fstream>
    9.  
    10. using namespace std;
    11.  
    12. ServerThread::ServerThread(int socketDescriptor, QObject *parent) : QThread(parent) {
    13. blockSize = 0;
    14.  
    15. tcpSocket = new QTcpSocket;
    16. tcpSocket->setSocketDescriptor(socketDescriptor);
    17.  
    18. this->remote_host_info.hostname = tcpSocket->peerName().toStdString();
    19. this->remote_host_info.address = tcpSocket->peerAddress().toString().toStdString();
    20. this->remote_host_info.port = tcpSocket->peerPort();
    21.  
    22. connect(tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
    23. connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    24. connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    25. connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
    26.  
    27. cout << "Host connected (" << this->remote_host_info.address << ":" << this->remote_host_info.port;
    28. cout << (this->remote_host_info.hostname.empty() ? "" : "[" + this->remote_host_info.hostname + "]") << ")" << endl;
    29.  
    30. // emit sendMessage(GD_MSG_SERVER_VERSION, "Game Rule Server Version 1.0");
    31.  
    32. QMap<QString, QVariant> val;
    33. val.insert(GD_MSG_SERVER_NAME, "File Transfer Server");
    34. val.insert(GD_MSG_SERVER_VERSION, "1.0");
    35.  
    36. #if defined(Q_WS_X11)
    37. val.insert(GD_MSG_SERVER_OPERATING_SYSTEM, "Linux");
    38. #elif defined(Q_WS_WIN)
    39. val.insert(GD_MSG_SERVER_OPERATING_SYSTEM, "Microsoft Windows");
    40. #elif defined(Q_WS_MACX)
    41. val.insert(GD_MSG_SERVER_OPERATING_SYSTEM, "Mac OS");
    42. #endif
    43. val.insert(GD_TCP_LOCAL_HOSTNAME, QHostInfo::localHostName());
    44. val.insert(GD_TCP_LOCAL_ADDRESS, tcpSocket->localAddress().toString());
    45. val.insert(GD_TCP_LOCAL_PORT, tcpSocket->localPort());
    46. val.insert(GD_TCP_PEER_NAME, tcpSocket->peerName());
    47. val.insert(GD_TCP_PEER_ADDRESS, tcpSocket->peerAddress().toString());
    48. val.insert(GD_TCP_PEER_PORT, tcpSocket->peerPort());
    49. emit sendMessage(GD_RETR_WELCOME_STRING, val);
    50. }
    51.  
    52. ServerThread::~ServerThread() {
    53.  
    54. }
    55.  
    56. void ServerThread::run() {
    57. while (tcpSocket->state() == QTcpSocket::ConnectedState) {
    58. if (tcpSocket->canReadLine()) {
    59. cout << "readClient();" << endl;
    60. }
    61. sleep(1);
    62. }
    63.  
    64. tcpSocket->disconnectFromHost();
    65. cout << "Host diconnected (" << this->remote_host_info.address << ":" << this->remote_host_info.port;
    66. cout << (this->remote_host_info.hostname.empty() ? "" : "[" + this->remote_host_info.hostname + "]") << ")" << endl;
    67.  
    68. if(tcpSocket->state() != QAbstractSocket::UnconnectedState)
    69. tcpSocket->waitForDisconnected();
    70. }
    71.  
    72. void ServerThread::onDisconnected() {
    73.  
    74. QList<QString> str_list = reinterpret_cast<Server*>(this->parent())->getData(tcpSocket->peerAddress());
    75.  
    76. cout << "FILE_LIST >> " << endl;
    77. for(QList<QString>::iterator iter = str_list.begin();
    78. iter != str_list.end(); ++iter) {
    79. cout << (*iter).toStdString() << endl;
    80. }
    81. cout << "<< FILE_LIST " << endl;
    82.  
    83. QMap<QString, QList<QString> > file_list = reinterpret_cast<Server*>(this->parent())->getData();
    84.  
    85. cout << "FILE_LIST from Host>> " << endl;
    86. for(QMap<QString, QList<QString> >::iterator hiter = file_list.begin(); hiter != file_list.end(); ++hiter) {
    87. // QHostAddress ha = hiter.key();
    88. QString addr = hiter.key();
    89. QList<QString> list = hiter.value();
    90. cout << "Host: " << addr.toStdString() << endl;//ha.toString().toStdString() << endl;
    91. for(QList<QString>::iterator iter = list.begin(); iter != list.end(); ++iter) {
    92. cout << (*iter).toStdString() << endl;
    93. }
    94. }
    95. cout << "<< FILE_LIST " << endl;
    96.  
    97. }
    98.  
    99. void ServerThread::onConnected() {
    100. }
    101.  
    102. void ServerThread::onMessage( const QString &msg, const QVariant &data ) {
    103. cout << endl;
    104. cout << std::setw(30) << std::setfill('-') << "MESSAGE BEGIN" << std::setw(30) << std::setfill('-') << "";
    105. cout.flush();
    106. cout << endl;
    107.  
    108. cout << "Received data......" << endl;
    109.  
    110. cout << msg.toStdString() << endl;;
    111. cout << " MESSAGE: " << msg.toStdString() << endl;
    112. cout << " DATA : " << data.toString().toStdString() << endl;
    113. if(msg == GD_RETR_WELCOME_STRING) {
    114. //}
    115. }
    116. else if(msg == GD_FILE) {
    117. if(data.type() == QVariant::Map) {
    118. //mutex.lock();
    119. QMap<QString, QVariant> val;
    120. val = data.toMap();
    121.  
    122. QString md5sum;
    123. QString filename;
    124. qint64 chunk = 0;
    125. qint64 size = 0;
    126. char *data = NULL;
    127. QByteArray byteArray;
    128. if(val.find("MD5SUM") != val.end())
    129. md5sum = val.value("MD5SUM").toString();
    130. if(val.find("CHUNK") != val.end())
    131. chunk = val.value("CHUNK").toInt();
    132. if(val.find("SIZE") != val.end())
    133. size = val.value("SIZE").toInt();
    134. if(val.find("FILENAME_NEW") != val.end())
    135. filename = val.value("FILENAME_NEW").toString();
    136. if(val.find("CONTENT") != val.end())
    137. byteArray = val.value("CONTENT").toByteArray();
    138.  
    139. cout << "MD5SUM: " << md5sum.toStdString() << endl;
    140. cout << "FILENAME: " << filename.toStdString() << endl;
    141. cout << "CHUNK: " << chunk << endl;
    142. cout << "SIZE: " << size << endl;
    143.  
    144. QString new_dir = QFileInfo(filename).absolutePath();//local_path + filename;
    145. QDir dir(new_dir);
    146. dir.mkdir(new_dir);
    147.  
    148. if(size > 0) {
    149. //QMessageBox::information(this, "File1", new_dir);
    150. QFile file(filename);
    151. //QMessageBox::information(this, "File", new_dir);
    152. if(!file.open(QIODevice::ReadWrite))
    153. cout << "Can't write to file '" << filename.toStdString() << "'" << endl;
    154. // QMessageBox::critical(this, "Can't write to file", new_dir);
    155. file.seek(chunk);
    156. file.write(byteArray.data(), size);
    157. file.close();
    158. }
    159. }
    160. }
    161. }
    162.  
    163. void ServerThread::sendMessage(const QString &msg, const QVariant &data) {
    164. if(tcpSocket == NULL) return;
    165. if(tcpSocket->state() != QAbstractSocket::ConnectedState ) return;
    166.  
    167. QByteArray block;
    168. QDataStream out(&block, QIODevice::WriteOnly);
    169. out.setVersion(QDataStream::Qt_4_2);
    170. //out.setVersion( out.version() ); // set to the current Qt version
    171. //out << (quint16) 0;
    172. out << (quint32) 0;
    173. out << msg;
    174. out << data;
    175. out.device()->seek(0);
    176. out << (quint32)(block.size() - sizeof(quint32));
    177. //out << (quint16)(block.size() - sizeof(quint16));
    178.  
    179. tcpSocket->write(block);
    180. tcpSocket->flush();
    181. cout << "Message Sent: " << msg.toStdString() << endl;
    182. }
    183.  
    184. void ServerThread::onReadyRead() {
    185. if (tcpSocket == NULL) return;
    186. if ( tcpSocket->state() != QAbstractSocket::ConnectedState ) return;
    187.  
    188. QDataStream in(tcpSocket);
    189. in.setVersion(QDataStream::Qt_4_2);
    190. //in.setVersion( in.version() ); // set to the current Qt version instead
    191.  
    192. if (blockSize == 0) {
    193. if (tcpSocket->bytesAvailable() < (int)sizeof(quint32)) return;
    194. in >> blockSize;
    195. reading_message = true;
    196. }
    197. if (tcpSocket->bytesAvailable() < blockSize) return;
    198. QString msgString;
    199. QVariant msgData;
    200. in >> msgString;
    201. in >> msgData;
    202.  
    203. onMessage( msgString, msgData );
    204. reading_message = false;
    205. blockSize = 0;
    206. if (tcpSocket->bytesAvailable() > 0) onReadyRead();
    207. }
    208.  
    209. void ServerThread::displayError(QAbstractSocket::SocketError socketError) {
    210. switch(socketError) {
    211. default:
    212. cerr << QString("The following error occurred: %1.").
    213. arg(tcpSocket->errorString()).toStdString() << endl;
    214. }
    215. }
    To copy to clipboard, switch view to plain text mode 

    Can anybody give me a hint/tip to solve the problem.

    Many thanks
    Regards

    NoRulez

  3. #3
    Join Date
    Oct 2009
    Location
    Brazil Maceió/Alagoas
    Posts
    24
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded Qt Jambi
    Platforms
    Unix/X11 Windows

    Default Re: File Transfer with QTcpServer and QTcpSocket

    Hi N1Rulez,

    I have the same problem, you was able to solve?

    Regards
    Missias

Similar Threads

  1. Replies: 3
    Last Post: 29th June 2007, 08:32
  2. Replies: 6
    Last Post: 8th January 2007, 10:24

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.