Чтение онлайн

ЖАНРЫ

QT 4: программирование GUI на С++

Саммерфилд Марк

Шрифт:

13 }

14 out.device->seek(0);

15 out << quint16(block.size - sizeof(quint16));

16 tcpSocket.write(block);

17 statusLabel->setText(tr("Sending request..."));

18 }

Слот sendRequest выполняется, когда объект QTcpSocket генерирует сигнал connected, уведомляя об установке соединения. Задача этого слота — сгенерировать запрос к серверу с передачей всей введенной пользователем информации.

Запрос является двоичным блоком следующего формата:

• quint16

размер блока в байтах (не учитывая данное поле),

• quint8 — тип запроса (всегда «S»),

• QString — пункт отправления,

• QString — пункт прибытия,

• QDate — дата поездки,

• QTime — примерное время отправления или прибытия,

• quint8 — признак времени отправления («D») или прибытия («А»).

Сначала мы записываем данные в массив типа QByteArray с именем block. Мы не можем писать данные непосредственно в QTcpSocket, поскольку мы не знаем размер блока, который будет отсылаться первым, пока не разместим все данные в блоке.

Сначала мы записываем 0 в поле размера блока и затем размещаем остальные данные. Затем мы делаем вызов seek(0) для устройства ввода—вывода (для установки на начало буфера QBuffer, создаваемого автоматически классом QDataStream), чтобы встать на начало массива байтов и переписать первоначальный 0 фактическим размером блока данных. Эта величина рассчитывается как размер блока за вычетом sizeof(quint16) (то есть 2), чтобы исключить поле с размером блока из общей суммы байтов. После этого мы вызываем функцию write для объекта QTcpSocket, чтобы отослать этот блок на сервер.

01 void TripPlanner::updateTableWidget

02 {

03 QDataStream in(&tcpSocket);

04 in.setVersion(QDataStream::Qt_4_1);

05 forever {

06 int row = tableWidget->rowCount;

07 if (nextBlockSize == 0) {

08 if (tcpSocket.bytesAvailable < sizeof(quint16))

09 break;

10 in >> nextBlockSize;

11 }

12 if (nextBlockSize == 0xFFFF) {

13 closeConnection;

14 statusLabel->setText(tr("Found %1 trip(s)").arg(row));

15 break;

16 }

17 if (tcpSocket.bytesAvailable < nextBlockSize)

18 break;

19 QDate date;

20 QTime departureTime;

21 QTime arrivalTime;

22 quint16 duration;

23 quint8 changes;

24 QString trainType;

25 in >> date >> departureTime >> duration >> changes >> trainType;

26 arrivalTime = departureTime.addSecs(duration * 60);

27 tableWidget->setRowCount(row + 1);

28 QStringList fields;

29 fields << date.toString(Qt::LocalDate)

30 << departureTime.toString(tr("hh:mm"))

31 << arrivalTime.toString(tr("hh:mm"))

32 << tr("%1 hr %2 min").arg(duration / 60).arg(duration % 60)

33 << QString::number(changes) << trainType;

34 for (int i = 0; i < fields.count; ++i)

35 tableWidget->setItem(row, i, new QTableWidgetItem(fields[i]));

36 nextBlockSize = 0;

37 }

38 }

Слот updateTableWidget

подсоединяется к сигналу readyRead класса QTcpSocket, который генерируется всякий раз при получении QTcpSocket новых данных от сервера.

Сервер пересылает нам список возможных железнодорожных рейсов, которые удовлетворяют критерию пользователя. Каждый рейс передается в виде одного блока, и каждый блок начинается с поля размера блока. Цикл forever необходим, потому что мы не обязательно получаем от сервера блоки по одному [7] . Мы можем получить целый блок или только его часть или полтора блока либо даже все блоки сразу.

Рис. 14.2. Блоки приложения Trip Server.

7

Ключевое слою forever обеспечивается Qt. Оно просто разворачивается в оператор for (;;).

Итак, как действует цикл forever? Если переменная nextBlockSize равна 0, это означает, что мы не прочитали размер следующего блока. Мы пытаемся прочитать его (предполагается, что имеется по крайней мере 2 байта). Сервер использует значение 0xFFFF в поле размера блока для указания на то, что все данные переданы, и поэтому, если мы обнаруживаем это значение, мы знаем, что достигнут конец.

Если размер блока не равен 0xFFFF, мы пытаемся считать следующий блок. Во-первых, мы проверяем наличие блока байтов необходимого размера. Если его нет, мы прерываем цикл. Сигнал readyRead будет вновь сгенерирован, когда станет доступно больше данных, и мы попытаемся повторить процедуру.

Если мы уверены, что получен целый блок, мы можем спокойно использовать оператор >> для QDataStream для извлечения относящейся к поездкам информации, и мы создаем элементы QTableWidgetItem с этой информацией. Полученный от сервера блок имеет следующий формат:

• quint16 — размер блока в байтах (не учитывая данное поле),

• QDate — дата отправления,

• QTime — время отправления,

• quint16 — длительность поездки (в минутах),

• quint8 — количество пересадок,

• QString — тип поезда.

В конце мы вновь устанавливаем переменную nextBlockSize на 0 для указания того, что размер следующего блока неизвестен и его необходимо считать.

01 void TripPlanner::closeConnection

02 {

03 tcpSocket.close;

04 searchButton->setEnabled(true);

Поделиться с друзьями: