Название: Параметры сигналов/слотов
Отправлено: niklep от Апрель 11, 2011, 08:58
Доброго времени суток. Недавно начал смотреть в сторону Qt для диплома. Пишу приложения (клиент/сервер), которые через сокет обмениваются данными. В данный момент работаю над усовершенствованием клиента. Для того, чтобы клиент при незапущенном сервере пытался устанавливать соединение, и при появлении в сети сервера подключался, создал таймер. При появлении сигнала таймера timeout() вызывался слот slotConnectToHost(), в котором происходило примерно следующее: tcpSocket->connectToHost(host,port); Но GUI при этом не отвечают в момент, когда по таймеру выполняются события. Решил часть обработки вынести в поток. Здесь возникла проблема с параметрами сигналов/слотов (не могу привести к такому виду, чтобы они совпадали). Буду благодарен, если кто-нибудь подкинет какую-нибудь идейку или хитрый прием. Описываю ситуацию: 1. Есть класс Client. class Client : public QWidget { Q_OBJECT private: QLineEdit* txtInput; quint16 nextBlockSize; QTextEdit* txtInfo; MyThread* timerThread;
public: Client(const QString& strHost, int nPort, QWidget *parent = 0, Qt::WFlags flags = 0); QString host; int port; QTcpSocket* tcpSocket;
private slots: void slotReadyRead (); void slotError (QAbstractSocket::SocketError); void slotSendToServer(); void slotConnected(); };
2. Есть класс MyThread. class MyThread : public QThread { Q_OBJECT
public: MyThread(QObject *parent); void run(Client* client); void stop(); bool getStopped(); void setStoppedToFalse();
private: QTimer* tcpTimer; volatile bool stopped; volatile bool timerExist;
private slots: void slotConnectToHost(Client *some_client); };
Слот slotConnectToHost в классе MyThread принимает параметром объект класса Client (так как сокет принадлежит именно объекту client): void MyThread::slotConnectToHost(Client *some_client) { some_client->tcpSocket->connectToHost(some_client->host,some_client->port); if (some_client->tcpSocket->waitForConnected()) this->stop(); }
Поэтому, логично, при соединении объектов (через connect) необходимо указывать слот slotConnectToHost именно с параметром: void MyThread::run(Client* client) { if (!stopped) { qDebug() << "Thread RUN"; if (!timerExist) { tcpTimer = new QTimer(this); connect(tcpTimer, SIGNAL(timeout()), SLOT(slotConnectToHost(client))); timerExist = true; } tcpTimer->start(5000); } }
Вот здесь и ошибка. Сигнал timeout не имеет параметров. Вроде внятно объяснил суть проблемы. Есть идеи?
Название: Re: Параметры сигналов/слотов
Отправлено: Mikhail от Апрель 11, 2011, 09:31
Client *some_client передай потоку в конструкторе или иной инициализирующей функции. А слот оставь без параметров.
Название: Re: Параметры сигналов/слотов
Отправлено: mutineer от Апрель 11, 2011, 09:45
При соединении сигнала и слота ты не передаешь параметры, которые будут при вызове слота, а указываешь их типы. Параметры в слот передает сигнал при испускании. Почитай внимательнее про сигналы и слоты
Название: Re: Параметры сигналов/слотов
Отправлено: niklep от Апрель 11, 2011, 21:49
Спасибо за совет, просто я далеко не ас в программировании, тем более ООП=) Решил проблему, все скомпилировалось и работает, но вот только такое ощущение, что программе наплевать на то, что я создал поток. Когда моя программа была однопоточной, GUI не отвечало именно в тот момент, когда срабатывал сигнал таймера timeout(). Вот я и решил все действия, выполняемые по таймеру, вынести в поток. Но сейчас понимаю, что где-то я прогадал... Класс клиента: client.cpp Client::Client(const QString& strHost, int nPort, QWidget *parent, Qt::WFlags flags) : QWidget(parent, flags), nextBlockSize(0) { // создаем GUI txtInfo = new QTextEdit; txtInput = new QLineEdit; txtInfo->setReadOnly(true); QPushButton* pcmd = new QPushButton("Send"); QVBoxLayout* pvbxLayout = new QVBoxLayout; pvbxLayout->addWidget(new QLabel("<H1>Client</H1>")); pvbxLayout->addWidget(txtInfo); pvbxLayout->addWidget(txtInput); pvbxLayout->addWidget(pcmd); setLayout(pvbxLayout); host = strHost; port = nPort;
// создаем сокет и создаем поток, который отвечает за коннект сокета к серверу tcpSocket = new QTcpSocket(this); timerThread = new MyThread(this, this); timerThread->run();
connect(tcpSocket, SIGNAL(connected()), SLOT(slotConnected())); connect(tcpSocket, SIGNAL(readyRead()), SLOT(slotReadyRead())); connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotError(QAbstractSocket::SocketError))); connect(pcmd, SIGNAL(clicked()), SLOT(slotSendToServer())); connect(txtInput, SIGNAL(returnPressed()), this, SLOT(slotSendToServer())); }
void Client::slotReadyRead() { // неважно }
void Client::slotError(QAbstractSocket::SocketError err) { QTime time; QSqlQuery query; query.prepare("INSERT INTO client (Time, Event, Code) VALUES (:time, 'Error', :code);"); query.bindValue(":time",time.currentTime().toString());
QString strError = time.currentTime().toString() + " Error: "; if (err==QAbstractSocket::HostNotFoundError) { strError = strError + "The host was not found"; query.bindValue(":code","The host was not found"); } else if (err==QAbstractSocket::RemoteHostClosedError) { strError = strError + "The remote host is closed"; query.bindValue(":code","The remote host is closed"); } else if (err==QAbstractSocket::ConnectionRefusedError) { strError = strError + "The Connection was refused"; query.bindValue(":code","The Connection was refused"); } else { strError = strError + tcpSocket->errorString(); query.bindValue(":code",tcpSocket->errorString()); } query.exec(); txtInfo->append(strError); // при возникновении ошибки (например когда сервер не запущен), если поток в данный момент не занят подключением (такая ситуация возникает если сервер был открыт и вдруг закрылся), вновь создать поток, который начнет искать подключение if (timerThread->getStopped()) { timerThread = new MyThread(this, this); timerThread->run(); } }
void Client::slotSendToServer() { // неважно }
void Client::slotConnected() { QTime time; QString mess = time.currentTime().toString()+" Received the connected signal"; txtInfo->append(mess);
QSqlQuery query; query.prepare("INSERT INTO client (Time, Event, Code) VALUES (:time, 'General', 'Received the connected signal');"); query.bindValue(":time",time.currentTime().toString()); query.exec(); }
Класс потока: mythread.cpp: MyThread::MyThread(QObject *parent, Client *some_client) : QThread(parent) { qDebug() << "New Thread"; temp_client = some_client; stopped = false; timerExist = false; }
void MyThread::setStoppedToFalse() { stopped = false; } bool MyThread::getStopped() { return stopped; }
void MyThread::run() { if (!stopped) { qDebug() << "Thread RUN"; if (!timerExist) { tcpTimer = new QTimer(this); connect(tcpTimer, SIGNAL(timeout()), SLOT(slotConnectToHost())); timerExist = true; } tcpTimer->start(5000); } }
void MyThread::stop() { qDebug() << "Thread STOP"; tcpTimer->stop(); stopped=true; }
void MyThread::slotConnectToHost() { temp_client->tcpSocket->connectToHost(temp_client->host,temp_client->port); if (temp_client->tcpSocket->waitForConnected()) this->stop(); }
main.cpp: static bool createConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("localhost"); db.setDatabaseName("test"); db.setUserName("root"); db.setPassword("root"); if (!db.open()) { qDebug() << "Cannot open database: " << db.lastError(); return false; } else qDebug() << "Open database OK"; return true; }
int main(int argc, char *argv[]) { QApplication app(argc, argv);
QFile file("config.xml"); QXmlInputSource source(&file); QXmlSimpleReader reader; SaxHandler handler; reader.setContentHandler(&handler); reader.parse(source);
if (!createConnection()) return -1;
Client client(handler.hostName, handler.portNumber); client.show(); return app.exec(); }
Вновь буду благодарен Вашим советам.
Название: Re: Параметры сигналов/слотов
Отправлено: mutineer от Апрель 12, 2011, 00:50
Не понял идеи. Что именно должно выполняться в отдельном потоке?
Название: Re: Параметры сигналов/слотов
Отправлено: niklep от Апрель 12, 2011, 07:13
В отдельном потоке создается таймер и каждые 5 секунд по сигналу таймера timeout() происходит попытка подключения к серверу. И так пока не произойдет подключение. При этом GUI лагает как раз каждые 5 секунд...
Название: Re: Параметры сигналов/слотов
Отправлено: mutineer от Апрель 12, 2011, 07:36
Гуй лагает потому что таймер-то тикает в отдельном потоке, а вот слот, который по таймеру пытается подключиться, выполняется все еще в гуи потоке и при ожидании подключения замораживает гуй. Ну и не жди подключения waitForConnected, юзай сигналы сокета connected() и socketError()
Название: Re: Параметры сигналов/слотов
Отправлено: niklep от Апрель 12, 2011, 09:24
А вот я думал, что если я описал слот slotConnectToHost в класса MyThread, то он в новом потоке будет выполняться... А как тогда мне понимать, какие действия будут выполняться в новом потоке, а какие в старом?
Название: Re: Параметры сигналов/слотов
Отправлено: mutineer от Апрель 12, 2011, 09:43
В новом потоке выполняется метод run() объекта QThread, все вызванные непосредственно оттуда методы, а так же слоты объектов, которые были перемещены в поток при помощи void QObject::moveToThread ( QThread * targetThread ) Более подробно почитай, например, тут: http://habrahabr.ru/blogs/qt_software/115830/ (http://habrahabr.ru/blogs/qt_software/115830/)
|