I am developing a GUI using Qt for a test UDP server application i wrote in C using the sockets interface available in unix. I aim of the GUI is to display all the messages (perror and fprintf) in either a "text edit box" or "text browser box". Here is my code,
//-----------------------------Main.Cpp------------------------------------------//
//-----------------------------QT specific headers-------------------------------//
#include "mainwindow.h"
#include <QApplication>
#include <QLabel>
//------------------------------------------------------------------------------------//
int main(int argc, char *argv[])
{
int sock_fd=0;
funct_class funct_class_obj;
QApplication a(argc, argv);
MainWindow w;
sock_fd=funct_class_obj.udp_config();
funct_class_obj.recv_fucnt(sock_fd);
return a.exec();
}
//--------------------------------End of main.cpp------------------------------//
//-----------------------------mainwindow.cpp----------------------------------//
//-----------------------------QT specific headers-----------------------------//
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QApplication>
#include <QLabel>
//-----------------------------------------------------------------------------//
//--------------------------------C program specific headers------------------//
#include <stdio.h>
#include <stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
//---------------------------------------------------------------------------//
unsigned char message[50];
QString mystr;
int funct_class:: udp_config()
{
int sock_fd=0;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
perror("\nError creating socket\n");
//mystr = "\nError creating socket\n";
else
{
fprintf(stdout, "\nSuccessfully created socket with descriptor:%d\n", sock_fd);
//mystr = "Successfully created socket";
struct sockaddr_in sock_addr = {0};
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = htons(PORT);
if (inet_aton("127.0.0.1", &sock_addr.sin_addr) < 0)
perror("\nError converting destination IP address from dotted decimal to binary\n");
// mystr = "Error converting destination IP address from dotted decimal to binary";
else
{
if (bind(sock_fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) // Bind function call binds the properties assigned above to the socket created earlier
perror("\nError binding the port and IP address with the socket\n");
//mystr = "Error binding the port and IP address with the socket";
else
fprintf(stdout, "\nBinding port and IP address successful\n");
// mystr = "Binding port and IP address successful";
}
}
return sock_fd;
}
void funct_class:: recv_fucnt(int sock_fd)
{
struct sockaddr_in recv_sockaddr = {0};
socklen_t recv_sockaddr_len = sizeof(recv_sockaddr);
int recv_data=0;
if ((recv_data = recvfrom(sock_fd, message, sizeof(message), 0x00, (struct sockaddr*)&recv_sockaddr, &recv_sockaddr_len)) <= 0)
perror("\nError receiving data/no data received\n");
//mystr = "Error receiving data/no data received";
else
fprintf(stdout, "\nSuccessfully received %d bytes of data\n", recv_data);
fprintf(stdout, "\nData is :%s", message);
//mystr = "Successfully received data";
mystr = QString::fromStdString((const char*)message);
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->testedit->setText(mystr);
//ui->mytextbrowser->setText((mystr));
}
MainWindow::~MainWindow()
{
delete ui;
}
//-----------------------------End of mainwindow.cpp---------------------------//
//------------------------------------mainwindow.h-----------------------------//
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#define PORT 5030
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
class funct_class{
public:
int udp_config();
void recv_fucnt(int sock_fd);
};
#endif // MAINWINDOW_H
//-------------------------------End of mainwindow.h-----------------------------//
The application is compiling fine, but i am facing problems when i execute it. It just freezes and even if i manage to get it out from freeze, i am not able to display the required string either in the "text edit box" or "text browser box". Please help.
P.S.: I am a newbie to GUI programming and Cpp.
The issue seems to be pretty clear:
ui->testedit->setText(mystr);
This is only called in the constructor and nowhere else. That means, when your mystr is updated, the widget will not be aware of it. It does not work like you seem to expect; it is not a one-time property binding so that the widget will be be notified automatically and transparently when the string changes. You have to do that work on your own.
Beside, you really ought to aim for using QtNetwork in a Qt based client rather than platform specific POSIX API with BSD sockets.
Related
Gist of the program: The server creates n client processes. On the client, the user enters a character string that is sent to the server. On the server, the string is processed as follows: the frequency of occurrence of vowels and numbers in the entered string is calculated. This information is then sent to the client.
Two programs (server and clients) were based on console editions(They works perfectly). When I add QT support to server, console clients and gui server works fine, but when i add gui to client - it stop working and i don't know what to do.
I guess the problem is not even named pipes, but clumsy code in terms of Qt (in client). The client outputs that a connection to the server has been established, and the server indicates that it cannot read a string from the pipe.
Server can create n gui clients and then, when i try to write string to client, client closes.
Code of the client:
gui_pipe_client.h:
#pragma once
#include <LotsOfLibraries...>
class Window : public QWidget {
Q_OBJECT // этот макрос должен включаться в классы, которые объявляют свои собственные сигналы и слоты
private:
HANDLE. m_hndlNP;
public:
Window(QWidget *parent = 0, HANDLE m_hndlNP = NULL);
private slots:
int OnSend();
private:
QLabel *lbl;
QLineEdit *textfield;
QPushButton *button_send;
};
gui_pipe_client.cpp:
#include "gui_pipe_client.h"
#include <QGridLayout>
#include <thread> //!NEW!
using namespace std;
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
Window::Window(QWidget *parent, HANDLE hndlNP)
: QWidget(parent), m_hndlNP(hndlNP){
QLineEdit *textfield = new QLineEdit(this);
textfield->setPlaceholderText("Enter string...");
QPushButton *button_send = new QPushButton("Send", this);
QPushButton *button_quit = new QPushButton("Quit", this);
lbl = new QLabel("kjiqofj13fjio2;3fjl;3jiofj2l3;fij3;fioj423j;fj32jf;o3f", this);
lbl->setWordWrap(true);
QGridLayout *grid = new QGridLayout(this);
grid->addWidget(textfield, 0, 0);
grid->addWidget(button_send, 1, 0);
grid->addWidget(button_quit, 2, 0);
grid->addWidget(lbl, 3, 0);
setLayout(grid);
connect(button_send, &QPushButton::clicked, this, &Window::OnSend); // привязка сигнала и слота
connect(button_quit, &QPushButton::clicked, qApp, &QApplication::quit); // signal - slot
}
int Window::OnSend() {
button_send->setEnabled(false);
char text[1024];
const QByteArray stringData = textfield->text().toUtf8();
//char text[100];
text[qMin(1023,stringData.size())]='\0';
std::copy(stringData.constBegin(),stringData.constBegin()+qMin(1023,stringData.size()),text);
printf("%s", text);
char answer[1024];
DWORD read_bytes;
DWORD written_bytes;
if (WriteFile(m_hndlNP, text, 1024, &written_bytes, NULL)) {
if (!ReadFile(m_hndlNP, answer, 1024, &read_bytes, NULL)) {
//printf("ReadFile failed with %d.\n", GetLastError());
system("pause"); // TEMPORARY
//return EXIT_FAILURE;
}
}
else {
printf("WriteFile failed with %d.\n", GetLastError());
system("pause"); // TEMPORARY
//return EXIT_FAILURE;
}
//cout << "Writting and reading was successful\n";
lbl->setText((QString) (answer));
// ***CLOSING PIPE CONNECTION***
CloseHandle(m_hndlNP);
//QApplication::quit();
}
main.cpp:
#include "gui_pipe_client.h"
using namespace std;
int main(int argc, char *argv[]) {
cout << "Client is launched\n";
HANDLE hndlNP = CreateFile(
TEXT("\\\\.\\pipe\\os_lab4_pipe"),
GENERIC_READ | GENERIC_WRITE,
NULL, // Prevents other processes from opening a file or device
NULL, // cannot be inherited by any child processes
OPEN_EXISTING,
NULL, // no attributes
NULL // no template
);
if (hndlNP == INVALID_HANDLE_VALUE) {
printf("CreateFile error\n");
cout << "CreateFile error\n";
//return EXIT_FAILURE;
}
cout << "Pipe connection established\n";
QApplication app(argc, argv);
Window window(0, hndlNP);
window.resize(600, 600);
window.setWindowTitle("Pipe client");
window.show();
return app.exec();
}
This is a Qt program. I am trying to run gcc command and get the result using _popen(on Windows). However, I get no output.
After debugging, I find that gcc command runs ok.
void editor::on_action_Compile_triggered()
{
QString str = "gcc \""+curFile+"\" -o \""+outputFile+"\" 2>&1"; //compile curFile
FILE *fp = _popen(str.toStdString().data(),"r");
if (!fp)
{
ui->Log->setText("Error."); //Log is a text browser
}
else
{
QString tmpStr = "";
char tmp[1024] = { 0 };
while (fgets(tmp, 1024, fp) != NULL) //read fp
tmpStr += (QString)tmp;
ui->Log->setText(tmpStr); //print to screen
}
_pclose(fp);
}
According to me, you are not having a problem. Your above code is working for me (as long as I declare a correct curfile and outputFile). You are not having an ouput because gcc has successfully compiled the file. You may want to verify if the file called outputFile has been produced. Indeed, when gcc succeeds, it does not ouput anything.
Otherwise, you might be having a problem with your signal/slot connexion which does not trigger the slot on_action_Compile_triggered(then please read on the complete code provided below)
To test it try to modify your curFile to point to an inexisting file and you will get an ouput error typical of gcc.
To check this, as for me, I created a QmainWindow with a QPushButton button (called pushbutton) and a QtextEdit (called Log). I provide my complete code below.
When I have an error (for instance the compiled file is not found. to emulate this, rename your curFile into a wrong file), I obtain this (using your code above).
When I do not have any error, I obtain nothing in the QTextEditcontrol but the outputFile executable file is produced by gcc in the directory:
Here is my code:
// QMainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void compile();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
// QMainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::compile);
}
MainWindow::~MainWindow()
{
QObject::disconnect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::compile);
delete ui;
}
void MainWindow::compile()
{
QString curFile("..\\..\\T0180694\\test.c");
QString outputFile("..\\..\\T0180694\\test.exe");
//copied all from your code
QString str = "gcc \""+curFile+"\" -o \""+outputFile+"\" 2>&1"; //compile curFile
FILE *fp = _popen(str.toStdString().data(),"r");
if (!fp)
{
ui->Log->setText("Error."); //Log is a text browser
}
else
{
QString tmpStr = "";
char tmp[1024] = { 0 };
while (fgets(tmp, 1024, fp) != NULL) //read fp
tmpStr += (QString)tmp;
ui->Log->setText(tmpStr); //print to screen
}
_pclose(fp);
//until here
}
//main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I am trying to get the serial data and display it on the GUI, that i made in the QT for that when I try to get the serial data and display it in the output, it shows me this garbage type value.
"?\u0004?#\u0004??\u0004?#\u001C??"
"?\u0004?#\u0004??\u0004?#\u001C???\u0004?#\u001C"
this whole code is below, but the culprit lines are "readserial" function in the last.
#include "dialog.h"
#include "ui_dialog.h"
#include <QSerialPort>
#include<string>
#include<QDebug>
#include<QMessageBox>
#include <QSerialPortInfo>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->label->setText("0");
arduino = new QSerialPort(this);
serialBuffer = "";
bool arduino_is_availabe = false;
QString arduino_uno_port_name;
foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts())
{
if(serialPortInfo.hasProductIdentifier() && serialPortInfo.hasVendorIdentifier())
{
if((serialPortInfo.productIdentifier() == arduino_uno_product_id) && (serialPortInfo.vendorIdentifier() == arduino_uno_vendor_id))
{
arduino_is_availabe = true;
arduino_uno_port_name = serialPortInfo.portName();
}
}
}
if(arduino_is_availabe)
{
qDebug() <<"Found the arduino port...\n";
arduino->setPortName(arduino_uno_port_name);
arduino->open(QSerialPort::ReadOnly);
arduino->setBaudRate(QSerialPort::Baud9600);
arduino->setBaudRate(QSerialPort::Data8);
arduino->setFlowControl(QSerialPort::NoFlowControl);
arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);
QObject::connect(arduino, SIGNAL(readyRead()), this, SLOT(readSerial()));
}else
{
qDebug() <<"Couldn't find the correct port for the arduino.\n";
QMessageBox::information(this, "serial port error", "couldn't open the serial port to arduino ");
}
}
Dialog::~Dialog()
{
if(arduino->isOpen())
{
arduino->close(); // Close the serial port if it's open.
}
delete ui;
}
void Dialog::readSerial()
{
QByteArray serialData;
serialData=arduino->readAll();
serialBuffer+=QString::fromStdString(serialData.toStdString());
qDebug()<<serialBuffer;
}
void Dialog::on_label_linkActivated(const QString &link)
{
ui->label->setText(serialBuffer);
}
I think your problem is right here:
arduino->setBaudRate(QSerialPort::Baud9600);
arduino->setBaudRate(QSerialPort::Data8);
You are setting the baud rate twice in a row, which makes no sense. In the second line you are setting a value of QSerialPort::Data8, so perhaps you are trying to define the setDataBits method. It would look like this:
arduino->setBaudRate(QSerialPort::Baud9600);
arduino->setDataBits(QSerialPort::Data8);
I am trying to figure out how to correctly use Qt TCP Sockets, as well as multithreading. I want, as a test project in support of something more complex but similar I will try in the future, to do the following: a simple application that either listens for exactly one incoming connection, or connects to a serversocket; next it prints everything it receives over that socket.
The situation where I connect to a serversocket (I use netcat with the -l option for this) works fine: everything netcat sends to my application is printed correctly. However, when I use my program to listen for that one incoming connection (generated by netcat), the connecting succeeds but then I get this runtime error:
QObject::connect: No such signal QThread::readyRead() in ..\QTcpTest\listener.cpp:8
Here is the entire code (don't mind the plain C I/O I use sometimes, I'll remove this later):
[ main.cpp ]
#include "peer.h"
#include <QCoreApplication>
#include <QThread>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static bool askIfServer();
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Peer *p;
int rval;
if (askIfServer())
p = new Peer;
else
p = new Peer(false);
rval = a.exec();
delete p;
return rval;
}
static bool askIfServer()
{
bool isServer;
char ibuf[BUFSIZ];
fputs("Choose \'host\' or \'join\': ", stderr);
fgets(ibuf, BUFSIZ, stdin);
ibuf[strlen(ibuf) - 1] = '\0';
fflush(stdin);
if (strcmp(ibuf, "host") == 0)
isServer = true;
else if (strcmp(ibuf, "join") == 0)
isServer = false;
else
{
fputs("Failure.\n", stderr);
exit(-1);
}
return isServer;
}
[ peer.h ]
#ifndef PEER_H
#define PEER_H
#include "listener.h"
#include <QObject>
#include <QThread>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QTcpServer>
class Peer : public QObject
{
Q_OBJECT
public:
static const quint16 PORT = 5483;
explicit Peer(bool isHost = true, QString hostname = "localhost", QObject *parent = 0);
public slots:
void acceptPeer();
private:
bool _isHost;
QTcpServer *_server;
QTcpSocket *_socket;
QThread *_lThread;
Listener *_listener;
};
#endif // PEER_H
[ peer.cpp ]
#include "peer.h"
Peer::Peer(bool isHost, QString hostname, QObject *parent) :
QObject(parent),
_isHost(isHost)
{
if (_isHost)
{
_server = new QTcpServer;
connect(_server, SIGNAL(newConnection()), this, SLOT(acceptPeer()));
_server->listen(QHostAddress::Any, PORT);
}
else
{
_socket = new QTcpSocket;
connect(_socket, SIGNAL(hostFound()), this, SLOT(acceptPeer()));
_socket->connectToHost(hostname, PORT);
}
}
void Peer::acceptPeer()
{
disconnect(this, SLOT(acceptPeer()));
if (_isHost)
{
_socket = _server->nextPendingConnection();
delete _server;
_server = NULL;
}
_lThread = new QThread;
_listener = new Listener(_socket);
_listener->moveToThread(_lThread);
_lThread->start();
}
[ listener.h ]
#ifndef LISTENER_H
#define LISTENER_H
#include <iostream>
#include <QObject>
#include <QByteArray>
#include <QtNetwork/QTcpSocket>
using std::cout;
class Listener : public QObject
{
Q_OBJECT
public:
explicit Listener(QTcpSocket *socket, QObject *parent = 0);
public slots:
void acceptData();
private:
QTcpSocket *_socket;
};
#endif // LISTENER_H
[ listener.cpp ]
#include "listener.h"
Listener::Listener(QTcpSocket *socket, QObject *parent) :
QObject(parent),
_socket(socket)
{
// I guess this is where it goes wrong
connect(_socket, SIGNAL(readyRead()), this, SLOT(acceptData()), Qt::QueuedConnection);
}
void Listener::acceptData()
{
QByteArray data = _socket->readAll();
cout << data.constData();
}
The thing I do not get is that the error message claims that I try to connect some signal from QThread (and I clearly do not: see listener.cpp:8); even stranger is that this only happens when the socket is created by the QTcpServer instance, not in the other case. What am I missing?
EDIT: SOLVED
See my own answer.
I do not use an extra thread anymore (see comment). The problem was deleting the QTcpServer instance in Peer::acceptPeer(), once a connection was established. I thought that I could delete it because I only need that one socket QTcpServer::nextPendingConnection() returns. Apparently this is wrong, the socket is probably destroyed as well, leaving me with a rogue pointer (I think) crashing the program. Now I only call _server->pauseAccepting(); right after I get the socket, and all works fine.
Here is the change I've made:
void Peer::acceptPeer()
{
disconnect(this, SLOT(acceptPeer()));
if (_isHost)
{
_socket = _server->nextPendingConnection();
_server->pauseAccepting(); // no deletion
}
_listener = new Listener(_socket);
}
I am working with Qt creator to make a GUI program that takes in different URLs and will download and display the html code.
The user can add different URLs to a listWidget. Then the user can select a specific URL and download the html which will be displayed beside the list of URLs.
The problem I am having is getting the text area to update after the reply is received.
main.cpp - Basically just shows the window.
#include <QtGui/QApplication>
#include "mainwindow.h"
#include "htmlmanager.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h - Pretty straight forward. Contains the object html that will be used to request the html from the inputted website.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "htmlmanager.h"
#include <QString>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
HtmlManager html;
private slots:
void on_addButton_clicked();
void on_actionExit_triggered();
void on_removeButton_clicked();
void on_downloadButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp - This is the beginning of the problem. If you look down at the "downloadButton_clicked()" function, you see that it fetches the html by sending a request. However, the reply isn't recieved before the next line so the text field is set to "".
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_addButton_clicked()
{
if(!ui->lineEdit->text().isEmpty())
{
ui->listWidget->addItem(ui->lineEdit->text());
}
else
{
qDebug() << "Input field is empty.";
}
}
void MainWindow::on_actionExit_triggered()
{
//doesn't do anything right now
}
void MainWindow::on_removeButton_clicked()
{
if(ui->listWidget->currentItem()) //If an item is selected
{
delete ui->listWidget->currentItem();
}
else
{
qDebug() << "No selection";
}
}
void MainWindow::on_downloadButton_clicked()
{
if(ui->listWidget->currentItem()) //If an item is selected
{
html.fetch(ui->listWidget->currentItem()->text());
ui->textBrowser->setText(html.str);
}
else
{
qDebug() << "No selection";
}
}
htmlmaneger.h
#ifndef HTMLMANAGER_H
#define HTMLMANAGER_H
#include <QObject>
#include <QDebug>
#include <QtNetwork>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QString>
class HtmlManager : public QObject
{
Q_OBJECT
public:
HtmlManager();
void fetch(QString Url);
QString str;
public slots:
void replyFinished(QNetworkReply* pReply);
private:
QNetworkAccessManager* m_manager;
};
#endif // HTMLMANAGER_H
htmlmanager.cpp - Once the reply is received, it stores the html in the QString "str"
#include "htmlmanager.h"
HtmlManager::HtmlManager()
{
m_manager = new QNetworkAccessManager(this);
connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
}
void HtmlManager::fetch(QString Url)
{
m_manager->get(QNetworkRequest(QUrl(Url)));
qDebug() << "Sending network request.";
}
void HtmlManager::replyFinished(QNetworkReply* pReply)
{
qDebug() << "Recieved network reply.";
QByteArray data=pReply->readAll();
str = data;
}
Is there an easy way to send the value of str to the MainWindow class once the reply is received, or is there a way for the onclick function to wait to update the text area until after a reply is received?
You definitely don't want to wait for a reply in your onClick() function. That will cause your program to be unresponsive until the network request comes it (which could very well take "forever").
One way to attack this would be to add a signal to your HtmlManager class. Something maybe called stringReceived. Then, in your mainwindow class you'd just need to add a line like this:
connect(html, SIGNAL(stringReceived(QString)), ui->textBrowser, SLOT(setText(QString));
QNetworkAccessManager don't provide synchronous or blocking approach. If you want to wait until reply has been received you have to go for waiting in a local event loop until reply finished signal invoked.
See the following link for turning asynchronous operation into synchronous one:
http://doc.qt.digia.com/qq/qq27-responsive-guis.html
In the section "Waiting in a Local Event Loop" they have shown an example using QNetworkAccessManager. You can use the same approach with timeout and local event loop.