Connect to WebSocket server from Qt UI with Echo Client Example - c++

I'm using Websocket Echo Server Example in CLI and it works fine. I'm trying to connect to this server from my Qt GUI project. I have MainWindow class with an appropriate slot
void MainWindow::on_pushButton_clicked()
{
qDebug() << "Push btn clicked";
EchoClient client(QUrl(QStringLiteral("ws://localhost:1234")));
}
and EchoClient files from CLI Websocket Echo Client Example.
The main problem is that that I can't connect to the server when I push button on the form. However, I see debug message "Push btn clicked". It is supposed to be printed "Hello, world!". But nothing happens, no errors. Even signal void EchoClient::onConnected() is not fired.
But if I move EchoClient client(QUrl(QStringLiteral("ws://localhost:1234"))); to main.cpp it connects:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
EchoClient client(QUrl(QStringLiteral("ws://localhost:1234")));
return a.exec();
}
I am completely new in C++ and Qt.
Why this happens? Is it something with processing threads in UI? What should I know?
Qt 5.4.

inside your on_pushButton_clicked() function you are creating the EchoClient (which is asynchronous) in the stack of the function. As soon as the function exits, the EchoClient object is destroyed from the stack.
You can think about different solutions like creating a private field in the MainWindow class
private EchoClient *client;
then set it to null in the MainWindow constructor:
this->client = NULL;
and only at this point doing something like this in your click() routine:
void MainWindow::on_pushButton_clicked()
{
qDebug() << "Push btn clicked";
if (this->client == NULL)
{
this->client = new EchoClient(QUrl(QStringLiteral("ws://localhost:1234")));
}
else
qWarning() << "Carefull, the client is already running";
}
Then it is up to you taking care of the like cycle of the client object you have created.
Probably you have to think when you want to destroy it, probably via a "reset" button routine.
I suggest this reading: http://doc.qt.io/qt-4.8/qapplication.html#details

Related

How to kill and rerun the main process in qt?

I have an app which can run another exe files. Some buttons and connect to the executeable files. When i click the button, start running the another program... this is good, but remains open the main window and the program still running. I want to achive that the main window stop running (or disapear) when another program is running, and when i exit from the other app, run (or appear and run) the main process.
I tried this:
void MainWindow::RunSys(QString sh)
{
this->close();
QProcess ps;
if(ps.execute(sh)<0)
{
QMessageBox messageBox;
messageBox.critical(nullptr,"Error",sh + "Error!");
}
this->show();
}
//I call the function this way:
QString sh = settings->value("Run").toString();
connect(pButton, &QToolButton::clicked, [=] { RunSys(sh); });
It's working on Linux. Opens the executable program but the main program is till running on Windows. What can I do?
I could not reproduce the bug, we'll need you to provide a complete, minimal and reproducible example that illustrates the bug.
If it can help you, I've written the following sample code based on yours:
Header
class MainWindow : public QMainWindow
{
Q_OBJECT
protected:
QPushButton * pb;
public:
MainWindow();
void runShellCmd(const QString & cmd);
};
Implementation
MainWindow::MainWindow()
{
// Build the main window
resize(600, 400);
pb = new QPushButton("run cmd", this);
this->setCentralWidget(pb);
// Create the command
QString cmd("ping 127.0.0.1 -n 6");
// Connect the signal
connect(pb, &QPushButton::clicked, [=](){runShellCmd(cmd);});
}
void MainWindow::runShellCmd(const QString & cmd)
{
QProcess ps;
this->close();
int exit_code = ps.execute(cmd);
switch(exit_code)
{
// Do what you want with exit code
default:;
}
this->show();
}
Main function
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
And it worked fine.
EDIT:
I have understood what's wrong. Your example with calc.exe has whispered me the issue :)
Actually, QProcess::execute() blocks until the command call returns. In this case, it returns right after the launch, not when the windows is closed. So you got the expected behaviour.
I'm afraid that it will be way much harder to do it this way. Because calc.exe being an external program, except if you can read kind of closing signal from an external program, you would not be able to detect when it is closed.

How to close the main window when a dialog appears

I was making a test program where the MainWindow serves as a login screen. User types in a username and password. If they match what the string is assigned to, then the Dialog appears. If it fails, a QMessageBox appears instead.
What my issue is when I want the Dialog to appear (the main program), I want the Login page to disappear. The Command close(); would just close everything.
Here is the code for the MainWindow.cpp (The Dialog is referenced in the header as a POINTER called mDialog)
void MainWindow::on_pushButton_clicked()
{
if (ui->lineEdit->text() == username)
{
if (ui->lineEdit_2->text() == password)
{
//This is where the Dialog appears
mDialog= new MyDialog(this);
mDialog->show();
}
}
else if (ui->lineEdit->text() != username || ui->lineEdit->text() ==NULL)
{
if (ui->lineEdit_2->text() != password || ui->lineEdit_2->text() == NULL)
{
QMessageBox::warning(this, "Error", "The username or password is incorrect.");
}
}
}
Here is the code for the main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
In Qt if the parent is destroyed, the children also, so if you go to this as a parameter to MyDialog, it will be destroyed. So that it is not destroyed do not pass the parent.
Change mDialog= new MyDialog(this) to mDialog= new MyDialog() and place close() after show().
The function would look like:
...
mDialog= new QDialog();
mDialog->show();
close();
...
I think you should be showing the dialog as the login and the main window as the main program. If login is a success, show main window, not the other way around. Closing the main window is going to close the program.
I have done what you are trying to do. You can do what I said above or another option is to create a login screen in the main window using a QLabel. You can add an image to the Qlabel (a solid color image or anything you like) and make it the size of the window to block the view of the main program. Then you can add your line edits and buttons or anything you want. If login is successful, the image, labels and buttons can be closed to reveal the main program. I checked input using regular expressions.
Use this->close() to close the current window, but in the constructor of MyDialog, don't pass anything to the constructor. By default, the constructor will pass 0 to the parent argument, so as a result, the dialog will not have a parent.
If your main program is the Dialog you can before it have been shown open the login dialog with username/password fields.
Pseudocode for the main function (LoginDialog and MainDialog inherits QDialog):
QApplication a(argc, argv);
LoginDialog lDialog;
lDialog.exec(); // Modal dialog behavior. Stopped at this line while it not closed (QDialog::close())
if (lDialog.getUsername() != username || lDialog.getPassword() != password)
{
return 0;
}
MainDialog mDialog;
mDialog.show();
return a.exec();
You can set window visibility to false.
mainwindow.setVisible(false)

Communicate with two instances of Qt program

I used in my Qt program code to avoid opening second instance:
#include "mainwindow.h"
#include <QApplication>
#include <QSharedMemory>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
const char* MEM_KEY = "42";
QSharedMemory sharedMem(MEM_KEY);
if (sharedMem.create(1024))
{
qDebug() << "Create shared memory";
}
else
{
if (sharedMem.error() == QSharedMemory::AlreadyExists)
{
qWarning() << "Already create. Exiting process";
return 1;
}
}
MainWindow w;
w.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint);
w.show();
return a.exec();
It works (this code block opening second instance of my aplication [it is automatically close]), but I want to send an message or signal from opened for a moment second instance to first instance to perform for ex. maximalize window of first instance. Could you tell me how to do this simply?
You can use QtSingleApplication for this purpose. For example:
int main(int argc, char *argv[])
{
QtSingleApplication a(argc, argv);
if (a.isRunning())
{
a.sendMessage("42");
qWarning() << "Already create. Exiting process";
return 1;
}
MainWindow w;
a.setActivationWindow(&w);
QObject::connect(&a, SIGNAL(messageReceived(QString))
, &w, SLOT(onMessageReceived(QString)));
w.show();
int ret = a.exec();
QObject::disconnect(&a, 0, &w, 0);
return ret;
}
...
void MainWindow::onMessageReceived(const QString &message)
{
// Do stuff
}
You are looking for IPC (inter process communication) and sadly, i don't think there is a portable way of doing this, except for creating a socket and listening on 127.0.0.1. But if you are on unix i do recommend using qdbus. That is what all the cool linux programs are doing ;)
On windows there is i believe something similar. (But this is all not built into qt.) You can find the handle of your window (HWND) and use sendmessage().
to send a custom message you'll have to declare your own WM_SECONDINSTANCE (or something similar) by:
#define WM_SECONDINSTANCE WM_USER
#define WM_SOMEOTHERMESSAGE WM_USER+1
or use an enum (i'm lazy).
This tells your existing instance that another instance has been opened. To handle it in qt have a look at this.
To find the HWND, i would just put it int your shared memory from your first instance.
(My windows-knowledge is a bit rusty, so sorry for any errors)

Network reply after program ends

I wrote program which gets source code of web page from url but i have problem, because this code is prints on screen when program is ending and I can't use data which i downloaded. I think that the problem is because program waits for SIGNAL(finished()). Is any way to process downloaded data in my program before ending?
void Get::getCode()
{
networkManager = new QNetworkAccessManager(this);
link = "http://example.com/";
networkManager->get(QNetworkRequest(QUrl(link)));
connect(networkManager, SIGNAL(finished(QNetworkReply*)), &process, SLOT(replyFinished(QNetworkReply*)));
//QDesktopServices::openUrl(QUrl(link));
}
...
void Process::replyFinished(QNetworkReply* pReply)
{
QString source(pReply->readAll());
printf("%s\n", source.toStdString().c_str());
}
...
int main(int argc, char *argv[]){
QApplication a(argc, argv);
Get get; get.getCode();
MainWindow window;
printf("test point\n");
return a.exec();
//return 0;}
"test point" is first on screen and later html code.
The network manager runs asynchronous, which means that your main thread continues immediately after calling the get() method.
You can solve this by setting up an event loop which waits until the download is completed:
QEventLoop loop;
connect(networkManager, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec(QEventLoop::ExcludeUserInputEvents);
That should give you the expected result.

Problems with QStatusBar->showMessage()

I'm writing a Qt GUI Application, but there's a strange error i can't figure out;
Here's the whole code:
main.cpp
#include "LevelEditor.h"
int main(int argc, char* argv[])
{
LevelEditor editor(argc, argv);
editor.go();
return 0;
}
LevelEditor.h
#ifndef LEVELEDITOR_H
#define LEVELEDITOR_H
#include <QtGui>
class LevelEditor
{
public:
LevelEditor(int argc, char* argv[]);
~LevelEditor();
void go();
protected:
QApplication* app;
QMainWindow* main_window;
QMenuBar* menu_bar;
QStatusBar* status_bar;
QWidget* central;
QMenu* menu_entry[3];
QFrame* about_frame;
QList<QAction*> file_actions;
QList<QAction*> edit_actions;
QList<QAction*> help_actions;
private:
};
#endif // LEVELEDITOR_H
LevelEditor.cpp
#include "LevelEditor.h"
#include <QStatusBar>
LevelEditor::LevelEditor(int argc, char* argv[])
{
//initialise main objects
app = new QApplication(argc, argv);
main_window = new QMainWindow();
menu_bar = main_window->menuBar();
status_bar = main_window->statusBar();
central = main_window->centralWidget();
about_frame = new QFrame();
//initialise menu entries and actions
menu_entry[0] = new QMenu(); //file
menu_entry[1] = new QMenu(); //edit
menu_entry[2] = new QMenu(); //about
//creating and connecting events to action
menu_entry[0]->setTitle("File");
file_actions.append(new QAction("New", menu_entry[0]));
file_actions.append(new QAction("Open", menu_entry[0]));
file_actions.append(new QAction("Save", menu_entry[0]));
file_actions.append(new QAction("Quit", menu_entry[0]));
QObject::connect(file_actions.back(), SIGNAL(triggered()), app, SLOT(quit()));
QObject::connect(file_actions.back(), SIGNAL(hovered()), status_bar, SLOT(showMessage("Quit this App"));
menu_entry[0]->addActions(file_actions);
menu_bar->addMenu(menu_entry[0]);
//edit menu
menu_entry[1]->setTitle("Edit");
edit_actions.append(new QAction("Cut", menu_entry[1]));
edit_actions.append(new QAction("Copy", menu_entry[1]));
edit_actions.append(new QAction("Paste", menu_entry[1]));
menu_entry[1]->addActions(edit_actions);
menu_bar->addMenu(menu_entry[1]);
//help menu
help_actions.append(new QAction("About", menu_entry[2]));
QObject::connect(help_actions.back(), SIGNAL(triggered()), about_frame, SLOT(show()));
menu_entry[2]->setTitle("Help");
menu_entry[2]->addActions(help_actions);
menu_bar->addMenu(menu_entry[2]);
about_frame->resize(400,300);
}
LevelEditor::~LevelEditor()
{
//dtor
}
void LevelEditor::go()
{
//nothing
main_window->showMaximized();
menu_bar->show();
status_bar->show();
app->exec();
}
This code compiles fine without errors.
Anyway, the debug console gives me a warning
QObject::connect : NO such slot &QStatusBar::showMessage("Quit this App")
The problem seems related to this line:
QObject::connect(file_actions.back(), SIGNAL(hovered()), status_bar, SLOT(showMessage("Quit this App"));
I've searched in "QStatusBar.h" for the showMessage function and it is declared, but can't be called neither with "." nor "->" (even if it's public). Also tried this:
QObject::connect(file_actions.back(), SIGNAL(hovered()), status_bar, SLOT(showMessage("Quit this App", 0));
and this:
QObject::connect(file_actions.back(), SIGNAL(hovered()), status_bar, SLOT(QStatusBar::showMessage("Quit this App"));
But to no avail, it just won't recognise the function.
Am i missing something here?
Thanks in advance.
EDIT: Solved, i was taking the hard way to show a status text instead of using QAction::setStatusTip, my bad.
You can't connect a signal to a slot with different signature. And you are not even using the proper connect syntax. The SLOT part should be:
SLOT(showMessage(const QString &))
It's to tell the meta object system what type(s) of parameters to send to the slot, not what concrete data to send.
In your case, you can't connect a signal with no parameter to a slot that expects one. You can achieve that by connecting the signal to your own slot and then call QStatusBar::showMessage from there.
You could use QSignalMapper to do what you want:
QSignalMapper * mapper = new QSignalMapper(this);
QObject::connect(file_actions.back(), SIGNAL(hovered()), mapper, SLOT(map()));
mapper->setMapping(file_actions.back(), "Quit this app");
connect(mapper, SIGNAL(mapped(const QString &)), statusBar, SLOT(showMessage(const QString &));
Using QSignalMapper allows you, to simply add another "hovered" messages without creating new slots for each. Simply for all other cases just use:
mapper->setMapping(yourAction/Button/Whater, "Your status message");
QObject::connect(yourAction/Button/Whater, SIGNAL(hovered/Other signal()), mapper, SLOT(map()))