Seite 1 von 1

Thread und TcpSocket

Verfasst: 26. März 2008 17:09
von stift
Hallo zusammen,

ich habe ein kleines Problem mit einen Programm. Ich habe ein kleines GUI-Programm geschrieben, welches einen QDial hat, die Werte des Sliders fragt das programm in regelmaessigen abstaenden ueber den QTcpSocket bei einen Server ab. Die Abfrage funktion und der TcpSocket laufen in einen eigenen Thread. Leider ist waehrend der Abfrage der Werte, die Ein-/Ausgabe blockiert.

Mir ist bekannt, dass der Code nicht sauber programmiert ist. Sorry

Kann mir jemand einen Tipp geben.

client.cpp

Code: Alles auswählen

#include <QtGui>
#include <QtCore>
#include <QtNetwork>
#include <QMessageBox>

#include "client.h"
//#include "consumer.h"


void Consumer::run()
{
		qDebug() << "TCP:" << endl;


  	usleep(5000000);

  	requestNewFortune();


}

int Consumer::get_value()
{
	return value;
}

void Consumer::set_dial(int i)
{
	dialog->lcd->display(i);
	dialog->dial->setSliderPosition(i);
}

void Consumer::set_dialog(Client *sdialog)
{
	
	
		QMessageBox::information(sdialog, tr("SET"), tr("SET"));	
		

	dialog = sdialog;
}


Client::Client(QWidget *parent)
    : QDialog(parent)
{
		QMessageBox::information(this, tr("Client"), tr("Client"));	
    hostLabel = new QLabel(tr("&Server name:"));
    portLabel = new QLabel(tr("S&erver port:"));

    hostLineEdit = new QLineEdit("127.0.0.1");
    portLineEdit = new QLineEdit("10000");
    portLineEdit->setValidator(new QIntValidator(1, 65535, this));

    hostLabel->setBuddy(hostLineEdit);
    portLabel->setBuddy(portLineEdit);

    statusLabel = new QLabel(tr("This examples requires that you run the "
                                "Fortune Server example as well."));

    getFortuneButton = new QPushButton(tr("Get Fortune"));
    getFortuneButton->setDefault(true);
    getFortuneButton->setEnabled(true);

    quitButton = new QPushButton(tr("Quit"));

    buttonBox = new QDialogButtonBox;
    buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
    	
    lcd = new QLCDNumber();
    	
    dial = new QDial();	
    dial->setMaximum(360);
    dial->setMinimum(0);
    dial->setNotchesVisible(true);
    dial->resize(400,400);
    dial->setEnabled(false);

		//cons_thread = new Consumer(this);
   	cons_thread.set_dialog(this);


  
	QMessageBox::information(this, tr("CONNECT"), tr("CONNECT"));	
    connect(hostLineEdit, SIGNAL(textChanged(const QString &)),
            this, SLOT(enableGetFortuneButton()));
    connect(portLineEdit, SIGNAL(textChanged(const QString &)),
            this, SLOT(enableGetFortuneButton()));
   // connect(getFortuneButton, SIGNAL(clicked()),this, SLOT(requestNewFortune()));
   connect(quitButton, SIGNAL(clicked()), this, SLOT(ende()));
    connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));


    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->addWidget(hostLabel, 0, 0);
    mainLayout->addWidget(hostLineEdit, 0, 1);
    mainLayout->addWidget(portLabel, 1, 0);
    mainLayout->addWidget(portLineEdit, 1, 1);
    mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
    mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
    mainLayout->addWidget(lcd, 4,0,1, 2);
    mainLayout->addWidget(dial, 5,0,1,2);
    setLayout(mainLayout);


    setWindowTitle(tr("Fortune Client"));
    portLineEdit->setFocus();
    
    QMessageBox::information(this, tr("START"), tr("START"));	
		cons_thread.start();
   
    
}

void Client::ende()
{
	QMessageBox::information(this, tr("ENDE"), "");
    	
		cons_thread.exit();
}



void Consumer::requestNewFortune()
{
	
	usleep(1000000);
	
		  	tcpSocket = new QTcpSocket(this);
  		
	connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readFortune()));
	connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
  connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
  	
    blockSize = 0;
    //QMessageBox::information(dialog, tr("request"), "");
    tcpSocket->connectToHost(tr("127.0.0.1"), 10000);
    exec();
}

void Consumer::readFortune()
{
		//QMessageBox::information(dialog, tr("Fortune Client"), tr("READ"));
	
    QDataStream in(tcpSocket);
    in.setVersion(QDataStream::Qt_4_0);

    if (blockSize == 0) {
        if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
            return;

        in >> blockSize;
    }

    if (tcpSocket->bytesAvailable() < blockSize)
        return;

    QString nextFortune;
    in >> nextFortune;

    if (nextFortune == currentFortune) {
        QTimer::singleShot(0, this, SLOT(requestNewFortune()));
        return;
    }

    currentFortune = nextFortune;
    
    //QMessageBox::information(dialog, tr("Fortune Client"), currentFortune);
    	
    dialog->statusLabel->setText(currentFortune);
    dialog->getFortuneButton->setEnabled(true);

    
   	dialog->dial->setValue(dialog->statusLabel->text().toInt());
	 	tcpSocket->disconnectFromHost();
	 
//run();

}

void Consumer::disconnected()
{
//	QMessageBox::information(dialog, tr("DISCONNECTED"), "");
		requestNewFortune();
	
}


void Consumer::displayError(QAbstractSocket::SocketError socketError)
{

    switch (socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        QMessageBox::information(this->dialog, tr("Fortune Client"),
                                 tr("The host was not found. Please check the "
                                    "host name and port settings."));
        break;
    case QAbstractSocket::ConnectionRefusedError:
        QMessageBox::information(this->dialog, tr("Fortune Client"),
                                 tr("The connection was refused by the peer. "
                                    "Make sure the fortune server is running, "
                                    "and check that the host name and port "
                                    "settings are correct."));
        break;
    default:
        QMessageBox::information(this->dialog, tr("Fortune Client"),
                                 tr("The following error occurred: %1.")
                                 .arg(tcpSocket->errorString()));
    }

		QMessageBox::information(dialog, tr("Fortune Client"), tr("host name and port settings."));
                                    
    QMessageBox::information(dialog, tr("ERROR"), "");
}

void Consumer::enableGetFortuneButton()
{
    dialog->getFortuneButton->setEnabled(!dialog->hostLineEdit->text().isEmpty()
                                 && !dialog->portLineEdit->text().isEmpty());
}
client.h

Code: Alles auswählen

#ifndef CLIENT_H
#define CLIENT_H

//#include "consumer.h"


#include <QDialog>
#include <QTcpSocket>
#include <QThread>


class QDialogButtonBox;
class QLabel;
class QLineEdit;
class QPushButton;
class QTcpSocket;
class QLCDNumber;
class QDial;

class Client;


class Consumer : public QThread
{
		Q_OBJECT
	
	private:

		int value;
		
	public:	
		Client *dialog;
		
	public slots:
    void requestNewFortune();
    void readFortune();
    void displayError(QAbstractSocket::SocketError socketError);
    void enableGetFortuneButton();	
    void disconnected();
		
	public:
	   void run();
	   int get_value();
	   void set_dial(int i);
	   void set_dialog(Client *sdialog);
	   
	   
    QTcpSocket *tcpSocket;
    QString currentFortune;
    quint16 blockSize;
};



class Client : public QDialog
{
    Q_OBJECT

public:
    Client(QWidget *parent = 0);


public slots:
	void ende();

public:
    QLabel *hostLabel;
    QLabel *portLabel;
    QLineEdit *hostLineEdit;
    QLineEdit *portLineEdit;
    QLabel *statusLabel;
    QPushButton *getFortuneButton;
    QPushButton *quitButton;
    QDialogButtonBox *buttonBox;
    
       
    
    Consumer cons_thread;

    
    QLCDNumber *lcd;
    QDial *dial;

};


#endif
[/b]

Verfasst: 26. März 2008 17:59
von solarix
Der Code ist nicht nur nicht sauber, sondern auch falsch:

Code: Alles auswählen

dialog->lcd->display(i);
dialog->dial->setSliderPosition(i); 
denn:
In GUI applications, the main thread is also called the GUI thread because it's the only thread that is allowed to perform GUI-related operations.
siehe http://doc.trolltech.com/4.3/threads.html

Aber du kannst doch in deinem Thread periodisch die Abfrage machen, und bei jeder Antwort ein signal emitten ('emit newValueReceived(42);') und dieses mit dem Dialog connecten. Dann hast du auch gleich keine zirkulaere Abhaengigkeit mehr..

Verfasst: 26. März 2008 18:43
von stift
Was muss ich dazu aendern? Ich habe bei (Q)Threads keine grosse Erfahrung.

Verfasst: 26. März 2008 19:12
von solarix
1) schmeiss den GUI-Kram aus dem Consumer
2) schreib "requestNewFortune()" so, dass nicht nur einmal, sondern periodisch eine Verbindung aufgebaut wird (QTimer)
3) anstelle des (haesslichen) "dialog->dial->setValue(dialog->statusLabel->text().toInt());" emittierst du ein eigenes Signal (z.B. "emit newValue(currentFortune.toInt());")
4) fuege deiner GUI ein Slot hinzu, welcher den Int-Wert in den Dial schreibt und connecte das Signal (des Threads) mit dem des Slots (GUI)

jetzt bist du dran..

Verfasst: 26. März 2008 19:17
von stift
Eine Frage noch:

In welcher Klasse ist die newValue()-Funktion? Und was macht die?

Verfasst: 26. März 2008 19:34
von solarix
ich dachte das haette ich geschrieben..
[...] ein eigenes Signal [..]
Also ein Signal deiner Klasse "Consumer". Das hat nichts mit Threads zu tun, sondern sind Qt-Grundlagen. Der Name war von mir ja nur ein Beispiel. Du kannst das Signal von mir aus auch "froehlicheWeihnachten" nennen, was jedoch die Wartungsfreundlichkeit nicht gerade erhoehen wuerde.