PDA

View Full Version : Passing strings to SLOT function



ashboi
7th September 2013, 04:51
Currently, I’ve written up an app that executes a client socket Slot at runtime that is used to sending data to the server. I’m wondering is it possible to pass a text string when a pushbutton is clicked, to a variable in the slot function? Example, when a pushbutton is clicked, a string containing “abcd” is passed to the message variable in the socket function, and the socket will send “abcd” over to the server.

I was wondering is the following codes would work,



QObject::connect(pushButton,SIGNAL(clicked(),this, SLOT(sendmsg()));
...
...
MainWindow::sendmsg()
{
strings associated with pushbutton...
}
...
...
MainWindow::socket()
{
.......
.......
send(...,sendmsg,...);
.......
.......
}

Lesiok
7th September 2013, 09:05
Something like this.
Slot definition somewhere :


slots :
void receiveMessage( QString message );
signal definition in MainWindow :


signals :
void sendMessage(QString message);
and method MainWindow::socket :

MainWindow::socket()
{
.......
.......
emit sendMessage(sendmsg());
.......
.......
}
Of course You must put somewhere this code :
QObject::connect(address_of_MainWindow,SIGNAL(send Message(QString)),addres_of_message_receiver, SLOT(receiveMessage( QString message )));

anda_skoa
7th September 2013, 09:52
If it is a fixed string per button, check QSignalMapper

Cheers,
_

ashboi
7th September 2013, 12:28
I probably should rephrase my question. Currently i have a socket thread(not slot), that executes at runtime when the app starts. the socket thread will wait in the background for a string to be passed to it from the pushbuttons, and when a pushbutton is pressed, example a string containing "abcd" will be pass to the socket thread's "send()" function and finally over to the server side, which is another device. Afterwhich, the socket will remain open until another button is pressed, and repeat the same procedure again.

I don't think the method Lesiok posted can do this, since receiveMessage is another slot within the same app to receive the strings.

Yes, it would be a fixed string per pushbutton, e.g pushbutton1-> "abcd", pushbutton2->
"defg".

zerokewl
7th September 2013, 12:45
Isn't what has been said already a solution?
Using SIGNALS/SLOTS?

Main Thread

signals:
void sendMessage(QString message);

SocketSender

slots:
void send(QString message);

And when you click a button, set it to


emit sendMessage("ABCD");

That would then EMIT the SIGNAL sendMessage('ABCD') and your SLOT would connect and get the QString value and send it appropriately?

After you create your socket thread, using the CONNECT to link the SIGNALS/SLOTS


connect(this, SIGNAL(sendMessage(QString), socketThread, SLOT(send(QString)));

ashboi
7th September 2013, 13:27
I'm not sure why I'm having an error saying that SIGNAL was not declared.


mainwindow.cpp:14:78: error: macro "SIGNAL" passed 3 arguments, but takes just 1
mainwindow.cpp: In constructor ‘MainWindow::MainWindow(QWidget*)’:
mainwindow.cpp:14:16: error: ‘SIGNAL’ was not declared in this scope
make: *** [mainwindow.o] Error 1



Here is my code

mainwindow.cpp

#include "mainwindow.h"
#include "ui_main.h"
#include "socketthread.h"
#include <QApplication>

using namespace std;

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, SIGNAL(sendMessage(QString), SocketThread, SLOT(send(QString)));
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::on_pushButton_clicked()
{
emit sendMessage("ABCD");
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui
{
class MainWindow;
}

class MainWindow : public QMainWindow {
Q_OBJECT

public:
MainWindow(QWidget *parent = 0);
~MainWindow();

signals:
void sendMessage(QString message);

private slots:
void on_pushButton_clicked();

private:
Ui::MainWindow *ui;

};

#endif // MAINWINDOW_H

socketthread.cpp

void SocketThread::run()
{
.....
.....
string_in = send(); // not sure how to connect the Qstring value here
.....
.....
}

socketthread.h

#ifndef SOCKET_H
#define SOCKET_H
#include <QThread>

class SocketThread : public QThread
{
Q_OBJECT

public slots:
void send(QString message);

private:
void run();
};
#endif // SOCKET_H

anda_skoa
7th September 2013, 13:48
Your error indicates that you are using SIGNAL wrong and indeed there is a closing parentheses missing.
And it is using the wrong name, the signal's name is sendMessage.

so over all it should be


SIGNAL(sendMessage(QString))


Or, as I said, even simpler using a QSIgnalMapper



{
ui->setupUi(this);

QSignalMapper *mapper = new QSignalMapper(this;

mapper->setMapping(ui->pushButton, "ABCD");
connect(ui->pushButton, SIGNAL(clicked()), mapper, SLOT(map()));

connect(mapper, SIGNAL(mapped(QString)), SocketThread, SLOT(send(QString)));
}


Assuming SocketThread is still owned by the main thread and has not been moved "into itself", you will also either require a Qt::QueuedConnection for the connect or mutex safe-guarding in the slot.

All in all I would advise to first make it work without threads. QTcpSocket works event based for a reason!

Cheers,
_

ashboi
7th September 2013, 14:30
Ah, missed that ')'.

Now I'm having another error at
connect(this, SIGNAL(sendMessage(QString)), SocketThread, SLOT(send(QString))); and I can't seem to figure out whats wrong with it.


mainwindow.cpp: In constructor ‘MainWindow::MainWindow(QWidget*)’:
mainwindow.cpp:15:58: error: expected primary-expression before ‘,’ token
make: *** [mainwindow.o] Error 1


On another note, my socket client is using zmq as a transport layer, and it executes at runtime when the app's GUI starts, currently, the I have the socket using 'cout' to check for errors/feeback from the server, and wait for messages to send over to the server. Initially, I was hoping that I can do something like, when a button is clicked, the strings "abcd" would be passed to the socket, and printed in the terminal, just like if a user were to manually type in "abcd" into the terminal, to show its actually working.

zerokewl
7th September 2013, 14:31
you forgot a ) here


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, SIGNAL(sendMessage(QString), SocketThread, SLOT(send(QString)));
}

it should be

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, SIGNAL(sendMessage(QString)), SocketThread, SLOT(send(QString)));
}

I wrote this as you Edited your last update fixing the missing bracket..

Can you please post your Constructor with new code?

ashboi
7th September 2013, 14:39
Sure thing, here it is


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, SIGNAL(sendMessage(QString)), SocketThread, SLOT(send(QString)));
}

zerokewl
7th September 2013, 14:52
Where is SocketThread instantiated from ?

Should you have something like?


MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
SocketThread *socketThread = new SocketThread();
connect(this, SIGNAL(sendMessage(QString)), socketThread, SLOT(send(QString)));
}

ashboi
7th September 2013, 15:17
socketThread is instantiated from main.cpp, I guess it would be easier if it was instantiated in mainwindow.cpp instead. No more errors on this.

But I have a question on how should I pass the string into my socket slot function? I tried
string_in = send(); but results in an argument not being provided.

Currently this is what I have

socketthread.cpp

void SocketThread::run()
{
...connect....
.................
while(1){
string string_in = ""; // I would like to pass the string into string_in
....
.......
}
exec();
}

zerokewl
7th September 2013, 15:33
The connect signal


connect(this, SIGNAL(sendMessage(QString)), SocketThread, SLOT(send(QString)));

Sends the Emitted QString to the socketThread::send(QString message).
Once you emit the signal and it is recieved from the socketThread::send(QString message) you could do this.


class socketThread{
private:
QString message;
}

void socketThread::send(QString message){
this->message = message;
}


And then inside your run


void SocketThread::run()
{
...connect....
.................
while(1){
string string_in = message.toStdString() //convert QString to string
....
.......
}

Lesiok
7th September 2013, 18:18
Currently this is what I have

socketthread.cpp

void SocketThread::run()
{
...connect....
.................
while(1){
string string_in = ""; // I would like to pass the string into string_in
....
.......
}
exec();
}


With this run() method You never receive any signal because thread event loop is never started. What are You doing in this "never ending" loop ?

ashboi
8th September 2013, 08:07
I managed to be error free, but Lesiok is right, no signal is being received when pushbutton is clicked. The terminal just waits for an input, I'm trying to get it to work in a way that when a pushbutton is clicked, the terminal would show that a string is being input and sent.

In that run function, I'm basically establishing a tcp socket using zeromq as a transport layer and sending/receiving message to and from the server (which is another device).

From a plain script perspective, when the function executes, the sockets will be established, and wait for a user to cin a specific string and proceed to send that off over to the server, and then it waits again for another input, and repeats the same process over and over.

I have had a brief look into QTcpSocket, but it seems that it would be a pain(correct me if I'm wrong) to get zmq to work with it, and I need zmq as a cross-platform client/server process to work, with inter-process (all of which I've gotten working).

wysota
8th September 2013, 08:48
To be honest your "socket thread" doesn't need to be a thread at all. If all the thread does is send a string over network then this can be done from the main thread, significantly simplifying your code.

anda_skoa
8th September 2013, 12:28
With this run() method You never receive any signal because thread event loop is never started.


Actually it should, unless ServerThread has been moveToThread() into itself.


What are You doing in this "never ending" loop ?

That is, of course, a good question.

Since the suggestion not to use a thread seems to have been ignored, I have to assume it is because of the 0MQ library, i.e. it only works with blocking calls.

All speculation until we see the actual code for ServerThread::run()

Cheers,
_

ashboi
9th September 2013, 13:06
Yes, that's correct, I'm using 0MQ for my sockets. There isn't any SERVER running on the app itself, the server script is running on another device.

Here is a simplified version of my socket thread function;

On serverthread.cpp

void SocketThread::send(QString message)
{
this->message = message;
}


void SocketThread::run()
{
string addr1 = "tcp://localhost:8886";
char *addr1_cstr1 = (char *) addr1.c_str();

zmq::context_t context(1);
zmq::socket_t sender(context, ZMQ_PUSH);
sender.connect(addr1_cstr1);

string addr2 = "tcp://localhost:8887";
char *addr_cstr2 = (char *) addr2.c_str();

zmq::socket_t reply(context, ZMQ_SUB);
reply.connect(addr_cstr2);
reply.setsockopt(ZMQ_SUBSCRIBE, "", 0);

string addr3 = "tcp://localhost:8888";
char *addr_cstr3 = (char *) addr3.c_str();

while(1)
{
string string_in = message.toStdString();
string to_send = "";

cout << "Input here: ";
getline(cin, string_in);
if(string_in == "test")
{
to_send = "test";
s_send(sender, (char *) to_send.c_str());
receive_msgs(reply);
to_send = "";
}
}
exec();
}


on socketthread.h

#ifndef SOCKET_H
#define SOCKET_H
#include <QThread>

class SocketThread : public QThread
{
Q_OBJECT
public slots:
void send(QString message);

private:
void run();
QString message;
};
#endif // SOCKET_H

anda_skoa
9th September 2013, 13:30
In this case you need to make sure that
1) the connect to send(QString) uses connection type Qt::DirectConnection (the receiver thread does not run an event loop).
2) use a QMutex member in SocketThread to protect both accesses to message


void SocketThread::send(QString message)
{
QMutexLocker locker(&mutex); // assuming the mutex member is called mutex
this->message = message;
}



while(1)
{
mutex.lock();
string string_in = message.toStdString();
mutex.unlock();
...


Cheers,
_

ashboi
10th September 2013, 13:17
I've tried the QMutex method, not sure what I might have done wrong, but for some reason I am still unable to receive the string when i click the pushbutton.

wysota
10th September 2013, 15:05
Could you elaborate on what you mean by "unable to receive the string"? Which code runs, which does not?

ashboi
11th September 2013, 14:01
Seems good now, I tested it with qdebug, and it was able to print out the emitted string. Sorry for the confusion.

I was wondering is it possible to use qdebug or something equivalent and use the message.toStdString with getline to use the emitted string as an input(cin)?
Correct me if I'm wrong about this, am I correct to say that qdebug can only be used for cout and not cin? On the other hand, if I'm using string string_in = message.toStdString(); as suggested, the app doesn't seem to be able to take the emitted string and do what it needs to do. The app just sits there waiting for an input, which it never gets when the pushbutton is clicked.

Also, I've tried using

QTextStream qtin(stdin);
qtin >> message;
doesn't seem to work.

wysota
11th September 2013, 15:17
For debug is for debugging output. If you don't want to use cin, you can use QTextStream with stdin. I assure you it works, apparently you're using it wrong.

anda_skoa
12th September 2013, 09:37
The app just sits there waiting for an input, which it never gets when the pushbutton is clicked.


You have two types of input: one through the button and the signal, one through reading from cin. The latter will of course block until it can read something from stdin.

Or did you mean it is hanging somewhere else?

Cheers,
_