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

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

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Сигналы в gui поток из дочернего  (Прочитано 19145 раз)
roginovicci
Гость
« : Апрель 27, 2009, 20:37 »

Извините если не в ту ветку написал. Ноги проблеммы растут из аплоада по фтп. Объясню проблему на примере. Есть кучка файлов, штук 200-300 т.е. много. Их надо залить на фтп по-одному. Т.е. я так понимаю надо отлавливать сигнал ftpCommandFinished и ftpCommandStarted. Причем делать это надо в отдельном потоке, иначе пока ждешь сигнала о завершении весь гуевый поток замораживается. Таким образом получается что саму заливку надо делать в отдельном потоке и по сигналам выставлять глобальную переменную в нужные значения. Примерно так:
Код:
class UploadWorker : public QThread
{
    Q_OBJECT
public:
    UploadWorker(QWidget *parent, QStringList *FileNameList, QFtp *ftp);
protected:
    void run();
private:
    FtpDialog *parent;
    QStringList *FileNameList;
    QFtp *ftp;
};

void UploadWorker::run(){
    int i;
    QFile *file;
    QFileInfo fi;

    for (i=0;i<FileNameList->count();i++){
// Ждем окончание предыдущей закачки, если вдруг не закончилась
        while (parent->upl_started)
            msleep(100);

        file = new QFile(FileNameList->at(i));
        fi.setFile(FileNameList->at(i));

        if (file->open(QIODevice::ReadOnly))
            ftp->put(file,fi.fileName());
// Ждем окончание закачки и удаляем объект на который сслается file
        while (parent->upl_started)
            msleep(100);

        file->close();
        delete file;
    }
}
Вот так реализованы сигналы в главном gui потоке :
Код:
ftpCommandStarted(int commandId)
{
    if (ftp->currentCommand() == QFtp::Put) {
        printf("!!!ftpCommandStarted\n");
        parent->upl_started=true;
    }
}
ftpCommandFinished(int commandId, bool error)
{
    if (ftp->currentCommand() == QFtp::Put) {
            printf("Downloaded file to current directory \n");
            parent->upl_started=false;
    }
}
Такая конструкция работать не будет ибо сигналы "эмитируются" во вспомогательном потоке. Как перенаправлять все сигналы связанные с объектом QFtp  из вспомогательного потока в главный?
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #1 : Апрель 27, 2009, 20:56 »

>>Вот так реализованы сигналы в главном gui потоке :
ты наверное хотел сказать слоты, а не сигналы.

>> ибо сигналы "эмитируются" во вспомогательном потоке.
неважно в каком потоке посылаются сигналы.
Записан

Юра.
Rcus
Гость
« Ответ #2 : Апрель 27, 2009, 20:58 »

Эм, а в чем проблема? сигналы работают и между потоками в двух режимах: очередь и блокировка.
А судя по постановке больше подходит что-то вроде Qt Concurrent/KDE ThreadWeaver
Записан
roginovicci
Гость
« Ответ #3 : Апрель 27, 2009, 21:10 »

да все верно - это  слоты. Проблемма в том, что слоты не отрабатываются.
Т.е. они ставятся в очередь и блокируются?
« Последнее редактирование: Апрель 27, 2009, 21:36 от roginovicci » Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #4 : Апрель 28, 2009, 01:49 »

>>Проблемма в том, что слоты не отрабатываются.
Добавь себе в профайл строчку:
CONFIG += console
Тогда при запуске приложения у тебя консоль будет, Qt в неё сообщит если соединение сигналов неудалось. Так по крайней мере можно часть проблем отловить или отсеить.
Записан

Юра.
roginovicci
Гость
« Ответ #5 : Апрель 28, 2009, 08:52 »

у меня за сборку отвечает cmake как добавить эту строку я не разумею, пока. Но я могу запустить софтину из консоли, и никаких мессаг о слотах не вываливается. Еще раз осмелюсь довести свое предположение. Новый поток => сигналы сыпятся в новом потоке и не отсылаются в главный.

ps А почему на форуме дефолтное время - Калининградское?
« Последнее редактирование: Апрель 28, 2009, 08:53 от roginovicci » Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #6 : Апрель 28, 2009, 09:51 »

Новый поток => сигналы сыпятся в новом потоке и не отсылаются в главный.

Тыкни меня носом в код, где в новом потоке испускаются сигналы? Покажи коннекты этих сигналов к слотам. Наконец, покажи больше кода.
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
roginovicci
Гость
« Ответ #7 : Апрель 28, 2009, 10:40 »

О! Вижу заинтересованность! Спасибо! Поехали:
Есть диалоговое окно:
ftpdialog.h
Код:
class FtpDialog : public QDialog, private Ui::FtpFrame
{
    Q_OBJECT
public:
    FtpDialog(QWidget *parent = 0);
    bool upl_started;
    QFtp *ftp;
private slots:
    void connectOrDisconnect();
    void ftpCommandFinished(int commandId, bool error);
    void ftpCommandStarted(int commandId);
    void updateDataTransferProgress(qint64 readBytes, qint64 totalBytes);
    void upload();

private:
    class UploadWorker *up;
};

class UploadWorker : public QThread
{
    Q_OBJECT
public:
    UploadWorker(FtpDialog *parent, QStringList *FileNameList, QFtp *ftp);
protected:
    void run();
private:
    FtpDialog *parent;
    QStringList *FileNameList;
    QFtp *ftp;
};
ftpdialog.cpp
Код:
void FtpDialog::ftpCommandStarted(int commandId)
{
    if (ftp->currentCommand() == QFtp::Put) {
        printf("ftpCommandStarted\n");
        upl_started=true;
    }
}
void FtpDialog::ftpCommandFinished(int commandId, bool error)
{
    if (ftp->currentCommand() == QFtp::Put) {
        if (error)
            QMessageBox::information(this, tr("FTP"),"error upload");
        else {
            QMessageBox::information(this, tr("FTP"),"Downloaded file to the current ftp directory");
            upl_started=false;
        }
    }
}
void FtpDialog::connectOrDisconnect()
{
     if (ftp) {
        ftp->abort();
        ftp->deleteLater();
        ftp = 0;
        return;
    }

    ftp = new QFtp(this);
    connect(ftp, SIGNAL(commandFinished(int, bool)),
            this, SLOT(ftpCommandFinished(int, bool)));
    connect(ftp, SIGNAL(commandStarted(int)),
            this, SLOT(ftpCommandStarted(int)));

     ftp->login();
}
void FtpDialog::upload(){

    QStringList *fl = new QStringList();

// заполняем fl строками - пути к файлам которые надо загрузить по ftp
    for (int i=0; i< files_list->count(); i++)
        fl->append(files_list->item(i)->text());
// создаем тред
    up = new UploadWorker(this,fl,ftp);
// и запускаем его
    up->start();
}
теперь что происходит в потоке:
Код:
UploadWorker::UploadWorker(FtpDialog *parent, QStringList *FileNameList, QFtp *ftp)
{
    this->parent=parent;
    this->FileNameList= new QStringList(*FileNameList);
    this->ftp=ftp;
}
void UploadWorker::run(){
    int i;
    QFile *file;
    QFileInfo fi;

    ftp->setTransferMode(QFtp::Passive);
    for (i=0;i<FileNameList->count();i++){
// если закачка еще не завершилась то ждем
        while (parent->upl_started){
            msleep(100);
            printf("while1");
        }
        file = new QFile(FileNameList->at(i));
        fi.setFile(FileNameList->at(i));

        if (file->open(QIODevice::ReadOnly)){
            printf("file opened. Transfer started\nFile is: %s\n",file->fileName().toLocal8Bit().constData());
            printf("ftp state is: %d\n", ftp->state());
            printf("ftp filename is: %s\n", fi.fileName().toLocal8Bit().constData());
            ftp->put(file,fi.fileName());
        } else {
            printf("file open error.\n");
        }
// ждем завершения закачки
        while (parent->upl_started){
            msleep(100);
            printf("while2");
        }
        file->close();
        delete file;
    }
}
Т.е. есть глобальная переменная upl_started - если она true (устанавливается в слоте ftpCommandStarted) то ждем окончания закачки и вызова слота ftpCommandFinished там она устанавливается в false.

Весьма признателен за желание наставить меня на путь истинный! =)

вот что в консоль сыпится после запуска:
Код:
file opened. Transfer started
File is: E:/110tsd.jpg
ftp state is: 4
ftp filename is: 110tsd.jpg
file opened. Transfer started
File is: E:/110tsd_.jpg
ftp state is: 4
ftp filename is: 110tsd_.jpg
file opened. Transfer started
File is: E:/110tsd_retro.jpg
ftp state is: 4
ftp filename is: 110tsd_retro.jpg
file opened. Transfer started
File is: E:/ava.jpg
ftp state is: 4
ftp filename is: ava.jpg
т.е. слоты не отрабатываются, иначе я бы в консоли от них услышал отклик.
« Последнее редактирование: Апрель 28, 2009, 12:25 от roginovicci » Записан
ритт
Гость
« Ответ #8 : Апрель 28, 2009, 13:52 »

Цитировать
UploadWorker::UploadWorker(FtpDialog *parent, QStringList *FileNameList, QFtp *ftp) // упс // : QThread(parent)
{
    this->parent=parent; // ы?
    this->FileNameList= new QStringList(*FileNameList); // хихи
    this->ftp=ftp;
}
void UploadWorker::run(){
    int i;
    QFile *file;
    QFileInfo fi;

    ftp->setTransferMode(QFtp::Passive);
    for (i=0;i<FileNameList->count();i++){
// если закачка еще не завершилась то ждем
        while (parent->upl_started){
            // я не знаю про межпотоковое блокирование и жду сто мс
            msleep(100);
            printf("while1");
        }
        file = new QFile(FileNameList->at(i));
        fi.setFile(FileNameList->at(i));

        if (file->open(QIODevice::ReadOnly)){
            printf("file opened. Transfer started\nFile is: %s\n",file->fileName().toLocal8Bit().constData());
            printf("ftp state is: %d\n", ftp->state());
            printf("ftp filename is: %s\n", fi.fileName().toLocal8Bit().constData());
            ftp->put(file,fi.fileName());
        } else {
            printf("file open error.\n");
        }
// ждем завершения закачки
        while (parent->upl_started){
            msleep(100);
            printf("while2");
        }
        file->close();
        delete file;
    }
}

мои комментарии, конечно, не особо по существу, но архитектуру определённо требуется пересмотреть. глядишь, заработает Шокированный
Записан
roginovicci
Гость
« Ответ #9 : Апрель 28, 2009, 14:52 »

Попробовал создать в новом потоке QFtp объект, и не удается законектится. Видимо QFtp как то сильно привязан к главному потоку Грустный

Константин, С удовольствием пересмотрю архитектуру, если вы предложите альтернативу. Суть такая: надо загружать на ftp по одному файлу из списка. Оповещание об окончании загрузки - сигнал ftpCommandFinished, ждать его в главном потоке = подвесить софтину. Загрузка из нового потока в силу архитектуры QT невозможна, есть варианты?
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #10 : Апрель 28, 2009, 17:19 »

>>Загрузка из нового потока в силу архитектуры QT невозможна, есть варианты?
прежде чем делать такие выводы, начни исправлять то, что Константин прописал (выделил жирным шрифтом).
Записан

Юра.
ритт
Гость
« Ответ #11 : Апрель 28, 2009, 18:16 »

> Попробовал создать в новом потоке QFtp объект, и не удается законектится. Видимо QFtp как то сильно привязан к главному потоку
неправда

> Оповещание об окончании загрузки - сигнал ftpCommandFinished, ждать его в главном потоке = подвесить софтину.
ждать сигнал? это только извращенцы в qxt таким занимаются.
налицо пробел в понимании механизма "сигнал-слот"

> Загрузка из нового потока в силу архитектуры QT невозможна
где такое сказано? не стОит делать громких заявлений, не ознакомившись как следует с документацией.

а теперь самое грустное: отдельный поток не нужен, какие-то смешные глобальные переключатели не нужны, сон в цикле не нужен (и кому вообще нужен поток, который всё время спит?).
а что же тогда нужно?
стринглист, куфтп, слот и ассистант...
...который утверждает, что
Цитировать
int QFtp::put ( const QByteArray & data, const QString & file, TransferType type = Binary )

This is an overloaded member function, provided for convenience.

Writes a copy of the given data to the file called file on the server. The progress of the upload is reported by the dataTransferProgress() signal.

The data is transferred as Binary or Ascii depending on the value of type.

The function does not block and returns immediately. The command is scheduled, and its execution is performed asynchronously. The function returns a unique identifier which is passed by commandStarted() and commandFinished().

When the command is started the commandStarted() signal is emitted. When it is finished the commandFinished() signal is emitted.
/* тут мысленно возвращаемся к первым трём цитатам */

и что же получается в итоге?

Цитировать
class FtpDialog : public QDialog, private Ui::FtpFrame
{
   Q_OBJECT

public:
   FtpDialog(QWidget *parent = 0);

private slots:
   void ftpCommandFinished(int commandId, bool error);
   void ftpCommandStarted(int commandId);
   void upload(const QStringList& slFilesToUpload);

private:
   QFtp* m_ftp;
   QStringList m_files;
};

FtpDialog::FtpDialog(QWidget *parent) : QDialog(parent)
{
   m_ftp = new QFtp(this);
   connect(m_ftp, SIGNAL(commandFinished(int, bool)),
         this, SLOT(ftpCommandFinished(int, bool)));
   connect(m_ftp, SIGNAL(commandStarted(int)),
         this, SLOT(ftpCommandStarted(int)));

   m_ftp->login();
   m_ftp->setTransferMode(QFtp::Passive); // нужно ли это здесь или стОит вынести в слот начала аплоада?
}

void Dialog::upload(const QStringList& slFilesToUpload)
{
   files += slFilesToUpload;

   commandFinished(0, false);
}

void FtpDialog::ftpCommandStarted(int commandId)
{
   if(m_ftp->currentCommand() == QFtp::Put)
      printf("QFtp::Put Started\n");
}

void FtpDialog::ftpCommandFinished(int commandId, bool error)
{
   if(m_ftp->currentCommand() == QFtp::Put)
   {
      /* 200-300 мессэйджбоксов - это, конечно, круто!
           if (error)
               QMessageBox::information(this, tr("FTP"),"error upload"); // QString QFtp::errorString () const - а это для кого?
           else
               QMessageBox::information(this, tr("FTP"),"Downloaded file to the current ftp directory");
      */
   }

   delete m_ftp->currentDevice();

   if(!m_files.isEmpty())
   {
      QString fname = m_files.takeFirst();
      QFile* file = new QFile(fname);
      if(file->open(QIODevice::ReadOnly))
      {
         m_ftp->put(file, QFileInfo(fname).fileName());
      }
      else
      {
         // можно приостановить передачу и спросить юзверя что делать
         // или переложить файл в конец очереди для повторной попытки,
         // или молча продолжить обход по списку

         delete *this; // интересно, будет так сиг11 или нет? Улыбающийся
      }
   }
}

ну, примерно так. в качестве простенькой очереди сойдёт. а если делать хорошо, то нужно бы ещё использовать результат m_ftp->put(...), проверять что за m_ftp->currentDevice() собираемся удалить, добавить обработку ошибок и взаимодействите с пользователем.
итого: архитектуру всё-таки следует пересмотреть. я бы вообще отделил весь этот код из диалога...диалог здесь явно ни к месту. пауза в очереди делается примитивно...надо только фантазии добавить.

удачи
Записан
roginovicci
Гость
« Ответ #12 : Апрель 28, 2009, 18:19 »

>>Проблемма в том, что слоты не отрабатываются.
Добавь себе в профайл строчку:
CONFIG += console
А вот кстати интересно как эту функцию отключить? Что надо в CMakeList.txt прописать?
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #13 : Апрель 28, 2009, 18:21 »

А вот кстати интересно как эту функцию отключить? Что надо в CMakeList.txt прописать?
Аналогичная проблема.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
roginovicci
Гость
« Ответ #14 : Апрель 28, 2009, 18:29 »

Спасибо за ответы, но на самом деле QFtp достаточно умен, что бы обрабатывать очередь загрузки самостоятельно. Ему можно наоткрывать кучу файлов и он будет их аплоадить по-очереди. Т.е. QFtp загружает только один файл. Объекты QFile потом удаляются через delete ftp->currentDevice(); Так что до 100 файлов можно одновременно открыть, а вот дальше, конечно могут начаться проблеммы, зависит от системы, сколько она позволяет открывать одновременно файлов.
ps Не стоит сильно придираться к коду, ясно же что в окончательном варианте QMessageBox и printf не будет. Но законектится по ftp не из главного потока пока не удалось. Попробую еще раз. Еще раз спасибо!
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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