Russian Qt Forum
Ноябрь 13, 2024, 10:19 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: как читать STDIN в сигнал-слотовом стиле? ответы здесь.  (Прочитано 20732 раз)
Denjs
Гость
« : Сентябрь 13, 2009, 23:19 »

Всем кто не разобрался с чтением в дочерней программе запущенной от QProcess потока stdin - посвящается.

Напомню проблему: Нас (мы - консольная прогрмамма) запустили как дочерний процесс с помошью QProcess, или чего-либо другого и собираются общаться с нами через stdin|stdout|stderr. Мы, естественно полностью QT-шная программа и хотим работать с данными потоками красиво, событийно-ориентированно и вообще не напрягаться.

Но пока, судя по всему, не напрягаясь если и получалось - то получалось не у многих.


В общем, т.к. вопросов было больше чем ответов -  отмечу 3 ключевых "пунктега" касающихся чтения stdin через QFile (по крайней мере для QT 4.5 под Linux).
1) чтение с помошью QFile данных из файла stdin - блокирующее. пока не поступит хоть один байт данных - повиснем.
2) но и узнать при этом, поступило-ли что вам на вход, тоже нельзя - QFile::byteasAvalable() не изменяет свого показания пока не считан хоть один байт данных из потока, а QFile::ReadyRead() не срабатывает принципиально.
3) и, наконец, касательно stdout: для того что бы данные из дочернего потока сразу попадали в QProcess - делайте в дочернем процесе после записи в stdOut функцию QFile::flush()

исходя из этих трех простых пунктов, тестового исходника из одного ранее появившегося поста и родился класс stdInReader.

Исходники ниже, в приложении - сорсы 2-х приложений для тестирования/демонстрации.

Класс простой - генерирует сигнал с данными, которые поступили на stdin, и обрабатывает 2 слота - для отсылки данных на stdout и stderr.

UseCase работы прост: Создаем объект, делаем ему init(), если ноль на выходе - вживляем его в нервную систему сигнал-слотов объектов вашего приложения и после делаем ему run(). Все: при поступлении данных на sdtin - побежал сигнал received_StdIn(QByteArray) а далее сами разберетесь т.д.

Так сказать, "в лучших традициях индийского быдлокодинга", представляею работающий прототип:

 stdInReader.h :
Код:
/* stdInReader.h
  under [GNU GPL v.2] from Denjs
  Простой класс-обертка для работы с потоками STDIN|STDOUT|STDERR.
  ver.2009.09.14 (естественно альфа, но работает и это главное)

  UTF-8 russian encoded file
 
*/
#include <QtCore>
#include <QThread>
#include <QFile>

class stdInReader : public QThread
 {
 Q_OBJECT

 public:
stdInReader(QObject * parent = 0);
// void run();
int init();//opend threads and became ready to work.
/*
     return
1 if some errors was happened
0 of all ok
-1 if can`t oopen stdIn
-2 if can`t oopen stdOut
-3 if can`t oopen stdErr
*/
void run();
 signals:
        void received_StdIn(const QByteArray &data);

 public slots:
int send_StdOut(const QByteArray &_data);
int send_StdErr(const QByteArray &_data);
 private:
QFile stdIn;
QFile stdOut;
QFile stdErr;
QByteArray data;
bool inited;
 };

 stdInReader.cpp :
Код:

/*  stdInReader.cpp :

  UTF-8 russian encoded file

*/
#include <stdInReader.h>

stdInReader::stdInReader(QObject * parent)
: QThread (parent)
 {
inited=false;
 }

int stdInReader::init()
 {
   inited=false;
   if (! stdIn.open(stdin, QIODevice::ReadOnly)) return -1;
   if (! stdOut.open(stdout, QIODevice::WriteOnly)) return -2;
   if (! stdErr.open(stdout, QIODevice::WriteOnly)) return -3;
   inited=true;
   return 0;
 };

//-----------------------------------------------------
void stdInReader::run()
 {

    if (!inited) return;
    while (true)
      {
/*/ while (stdIn.bytesAvailable()==0) //<------- ВНИМАНИЕ! так - НЕ работает... да) QT 4.5
                   //почему-то пока не начнешь читать, размер буфера не обновится...
this->msleep(1000);
/* - */
data=stdIn.read(1); //тут "блокирующее чтение"... потому никаких sleep не надо
if (stdIn.bytesAvailable()>0)
  data.append(stdIn.read(stdIn.bytesAvailable()));//и только тут буфер покажет сколько нам послупило данных. лол)
emit received_StdIn(data);

/*-/
////------ далее - демо код для примера ----------
//// что бы оно заработало - надо stdOut.open... раскомментировать выше...
stdOut.write(QByteArray().append(QString("[")));
stdOut.write(data);
stdOut.write(QByteArray().append(QString("]")));
stdOut.flush();//отправим сразу...
/* - */
      };
    return;
 }

//-----------------------------------------------------
int stdInReader::send_StdOut(const QByteArray &_data)
  {
int rz=stdOut.write(_data);
stdOut.flush();
return rz;
  }

//-----------------------------------------------------
int stdInReader::send_StdErr(const QByteArray &_data)
  {
int rz=stdErr.write(_data);
stdErr.flush();
return rz;
  }
ну а дальше - предлагаю довести класс до ума)
 - как минимум надо сделать нормальное завершение.
 - можно сделать его наследником QIODevice - но зачем? впрочем как решите - пусть так и будет. главное что бы сорсы обновлялись... пока выкладывайте здесь, если кто будет обновлять... дальше посмотрим.
« Последнее редактирование: Сентябрь 13, 2009, 23:37 от Denjs » Записан
Denjs
Гость
« Ответ #1 : Сентябрь 13, 2009, 23:55 »

Пока замечена одна проблема.

Не корректно (а вернее "вообще никак") не обрабатывается закрытие потока stdin...
в иттоге - запуск testcon.bin из следующего bash скрипта вгоняет программу в страшный бесконечный цикл...
Код:
#!/bin/bash

./testcon.bin <<ENDSS
hello!
how are you!
ENDSS

исправлять уже сегодня не буду. на неделе если дойдут руки если никто не исправит )
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #2 : Сентябрь 14, 2009, 00:27 »

Оформи тему в качестве статьи и я перенесу её в раздел Уроки и статьи и выложу в ВИКИ
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
Denjs
Гость
« Ответ #3 : Сентябрь 14, 2009, 00:51 »

ок... нашел требования... на днях как доберутся руки - сделаю..
« Последнее редактирование: Сентябрь 14, 2009, 00:55 от Denjs » Записан
merke
Гость
« Ответ #4 : Январь 26, 2011, 14:07 »

Но всё же, как считать stdin в Qt приложении?
Записан
Denjs
Гость
« Ответ #5 : Январь 26, 2011, 14:46 »

Но всё же, как считать stdin в Qt приложении?
гм... o_O как написано в классе -
Код:
QFile stdIn;
stdIn.open(stdin, QIODevice::ReadOnly)
не?
« Последнее редактирование: Январь 26, 2011, 14:55 от Denjs » Записан
merke
Гость
« Ответ #6 : Январь 26, 2011, 18:35 »

К примеру делаю вот так:

Код
C++ (Qt)
   QFile stdIn;
   stdIn.open(stdin, QIODevice::ReadOnly);
   QByteArray data;
   data = stdIn.readAll();
   qApp->processEvents();
   ui->lineEdit->setText(data);

Гуй напрочь зависает, а если взять и закрыть родительский процесс гуй у дочернего отмирает и выводится то чтобы было в stdin
Записан
merke
Гость
« Ответ #7 : Январь 26, 2011, 19:12 »

Посмотрите пожалуйста, вот этот код. Ну ни чего не работает(((
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #8 : Январь 26, 2011, 19:56 »

>>data = stdIn.readAll();
вот он и читает до посинения.
Записан

Юра.
Denjs
Гость
« Ответ #9 : Январь 26, 2011, 20:31 »

Александр, я вижу ваше недовольство/недоумение. я вижу ваш код.

но я не вижу чем именно вы недовольны, и какое поведение программы вы видите правильным?

например сейчас, я могу п едложить вам в родительской программе закрывать файл, который перенаправляется в дочерний stdin) но чувствуется мне - это не то что хотите.

опишите идеальное я вас поведение и задачи/цели/среду применения?

---------------------
readAll() скорее всего не работает, по тем-же причинам, что и описаны в пунктах 1 и 2 в "3 ключевых "пунктега" касающихся чтения stdin через QFile ". пока не прочитан хоть один байт - вы не сможете понять сколько там у вас данных.
и "зависните в операции чтения", если данных нет.
Ещё раз обратите внимание на ту часть исходного кода, которая ...
Код:
	data=stdIn.read(1); //тут "блокирующее чтение"... потому никаких sleep не надо
if (stdIn.bytesAvailable()>0)
  data.append(stdIn.read(stdIn.bytesAvailable()));//и только тут буфер покажет сколько нам послупило данных. лол)
попробуйте так читать, но боюсь и это вас не устроит) потому что понять есть ли данные нав ходе ожно толкьо начав чтение. но начав чтение пустого потока вы повисните.

потому предлагаю дописать вам в исходный класс буфер, и функцию readAll() для данного класса. Включая byteaAvalable().
Но помните что тут вы имеете дело с потоками, а QByteArray очень не любит когда к нему из 2-х потоков обращаются. потому смотрите ещё и что такое мьютексы.

и работайте с входным потоком через этот класс.

---------------------
лучше расскажите зачем и в какой ситуции вы хотите это использовать,
« Последнее редактирование: Январь 27, 2011, 01:05 от Denjs » Записан
merke
Гость
« Ответ #10 : Январь 27, 2011, 08:36 »

В общем рассказываю всю историю. Имеется допустим два приложения. Одно сервисное, оно запускает второе приложение и они должны между собой общаться служебными командами. В первой версии общение происходило по сокетам, далее решено было перевести всё на Pipe, но и эту идею исключили, в конечном случае остановились на использовании stdin/stdout/stderr. С дочернего процесса я спокойно пишу в stdin & stderr. В родительском я запросто всё это отлавливаю посредством сигналов класса QProcess. А вот уже прочитать stdin в дочернем приложении я и не могу.
Идея следующая: будет таймер с интервалом примерно 5 секунд. В обработчике таймера будет проверяться на наличие данных в stdin. Если такие имеются считывать их, определять что за команда нам поступила от сервисного приложения и производить определенные действия. На момент пока производятся эти самые "определенные действия", таймер будет останавливаться, а после их выполнения таймер будет заново перезапускаться.

Так вот, если в обработчике таймера писать следующий код:

   
Код
C++ (Qt)
forever
     {
       data=stdIn.read(1);
       if (stdIn.bytesAvailable()>0)
          data.append(stdIn.read(stdIn.bytesAvailable()));
       emit received_StdIn(data);
     }

Всё к едрени фени зависает. Ни что не могу понять...
Записан
Denjs
Гость
« Ответ #11 : Январь 27, 2011, 11:47 »

Всё к едрени фени зависает. Ни что не могу понять...
Прально, зависает до следующей порции данных ))  вы де перечитали пост стартующий тему и понимаете что чтение с стдина - "блокирующее", что означает что мы повиснем пока не получим данных?
вы думаете почему я это все поместил в отдельный поток внутри QThread ?!  что бы оно там себе висло до поступления следующей порции данных или закрытия канала - и никому это не мешало.

почему вы упорно не хотите просто взять и использовать класс представленный в начале топика?
принимайте сигналы им генерируемые, буферизируйте их, и каждые 5 секунд по вашему таймеру смотрите что там у вас есть в вашем входном буфере - и будет вам счастье.

« Последнее редактирование: Январь 27, 2011, 15:02 от Denjs » Записан
merke
Гость
« Ответ #12 : Январь 27, 2011, 18:00 »

Спасибо!!!
Записан
asvil
Гость
« Ответ #13 : Июнь 30, 2011, 18:30 »

О кстати, здесь то можно использовать QSocketNotifier примерное вот так:
Код:
class Receiver : public QObject
{
 Q_OBJECT
slots:
void readyRead()
{
QTextStream input(stdin);
QString msg = input.read(256);
// TODO
}
}

Receiver recevier;
QSocketNotifier *noti = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, qApp);

    noti->connect(noti, SIGNAL(activated(int)), &recevier, SLOT(readyRead()));
« Последнее редактирование: Июнь 30, 2011, 19:00 от Филоненко Михаил » Записан
asvil
Гость
« Ответ #14 : Июль 25, 2011, 13:36 »

 Грустный Под винду socketnotifier не работает
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.199 секунд. Запросов: 22.