Using QThread , Signal() , Slot() So GUI won't freeze
-
I try to run task upon button press the task should run on thread so the GUI won't freeze , I have mythread class button
and 2 functions that will run in chain on after another ,
function 1 - run_packet_task() calls
function 2 - packet_handler(arg1,arg2...etc) - will update the guifunction 1 should start running when button clicked
function 2 called by function 1 & updates the guiI don't understand how to set this up
// on On clicked , run on_Start_Capture_pushButton_clicked
connect(ui->Start_Capture_pushButton,&QPushButton::clicked ,this ,&MainWindow::on_Start_Capture_pushButton_clicked);// mySignal calls run_packet_task - signal emitted, slot executed
connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task);Where should I implement signal and slot function so the gui will stay responsive
on_Start_Capture_pushButton_clicked
is implemented in mainwindow.cppwhen the button is clicked, it triggers the on_Start_Capture_pushButton_clicked slot, which starts the thread. As the thread runs, it emits the mySignal, which in turn executes the run_packet_task slot in the MainWindow class. Inside the run_packet_task slot, you can call the packet_handler function or perform any other desired operations.
here is all the files :
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMutex> #include <QString> #include "mythread.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class CaptureThread; // Forward declaration class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_Start_Capture_pushButton_clicked(); void run_packet_task(); void on_Stop_Capture_pushButton_clicked(); signals: void startCapture(); void mySignal(); private: Ui::MainWindow *ui; QMutex uiMutex; mythread *thread; //CaptureThread* captureThread; }; #endif // MAINWINDOW_Hmythread.h
#ifndef MYTHREAD_H // mythread.h #define MYTHREAD_H #include <QThread> class mythread : public QThread { Q_OBJECT public: explicit mythread(QObject *parent = nullptr); void run() override; signals: void mySignal(); public slots: void on_Start_Capture_pushButton_clicked(); void mySlot(); }; #endif // MYTHREAD_Hmain.cpp
#include "mainwindow.h" #include <QApplication> #include "mythread.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; mythread thread; w.show(); return a.exec(); }mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <iomanip> #include <qmessagebox.h> #include <iostream> #include <WinSock2.h> #include <pcap.h> #include <ws2tcpip.h> #include <iphlpapi.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") #define NUM_PACKETS_TO_CAPTURE 10 #include <sstream> #include <QMutex> #include <QThread> #include "mythread.h" QMutex uiMutex; Ui::MainWindow* ui; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ::ui = ui; ui->setupUi(this); // Create an instance of MyThread thread = new mythread(this); // on On clicked , run on_Start_Capture_pushButton_clicked connect(ui->Start_Capture_pushButton,&QPushButton::clicked ,this ,&MainWindow::on_Start_Capture_pushButton_clicked); // mySignal calls run_packet_task - signal emitted, slot executed connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task); //connect(ui->Start_Capture_pushButton, &QPushButton::clicked, this, &MainWindow::on_Start_Capture_pushButton_clicked); pcap_if_t* alldevs = nullptr; // Initialize the pointer to nullptr char errbuf[PCAP_ERRBUF_SIZE]; if (pcap_findalldevs(&alldevs, errbuf) == -1) { std::cerr << "Error finding network interfaces: " << errbuf << std::endl; //return nullptr; // Return nullptr on error } for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { // Display the adapter index and description ui->Choose_Adapter_comboBox->addItem(dev->description); // Increment the adapter index for the next adapter } } MainWindow::~MainWindow() { delete ui; } void packet_handler(u_char* user_data, const struct pcap_pkthdr* packet_header, const u_char* packet_data) { // Cast the user_data to a pointer of the desired type, if needed // Example: MyData* data = reinterpret_cast<MyData*>(user_data); // Create a stringstream to hold the packet information std::stringstream packetInfo; // Add packet information to the stringstream packetInfo << std::endl; packetInfo << "Packet Information:" << std::endl; packetInfo << "Capture Length: " << std::setw(5) << packet_header->caplen << std::endl; packetInfo << "Packet Length: " << std::setw(5) << packet_header->len << std::endl; packetInfo << "Packet Data:" << std::endl; // Print packet data as hexadecimal and ASCII int byte_count = 0; for (int i = 0; i < packet_header->caplen; i++) { packetInfo << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(packet_data[i]) << " "; byte_count++; // Add extra space after every 8 bytes for better readability if (byte_count % 8 == 0) packetInfo << " "; // Add newline after every 16 bytes and print ASCII representation if (byte_count % 16 == 0) { packetInfo << "| "; for (int j = i - 15; j <= i; j++) { if (packet_data[j] >= 32 && packet_data[j] <= 126) packetInfo << static_cast<char>(packet_data[j]); else packetInfo << "."; } packetInfo << std::endl; } } // Convert the packetInfo stringstream to a string std::string packetInfoString = packetInfo.str(); QString packetInfoQString = QString::fromStdString(packetInfoString); // Use QMutexLocker to lock the UI access while appending the text QMutexLocker locker(&uiMutex); ui->Packet_textBrowser->append(packetInfoQString); }void MainWindow::run_packet_task() { QString selectedDev_qt = ui->Choose_Adapter_comboBox->currentText(); char errbuf[PCAP_ERRBUF_SIZE]; pcap_if_t* alldevs = nullptr; pcap_if_t* selectedDev = nullptr; for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { if (selectedDev_qt == dev->description) { selectedDev = dev; break; } } pcap_t* handle = pcap_open_live(selectedDev->name, BUFSIZ, 1, 1000, errbuf); if (handle == nullptr) { std::cerr << "Error opening interface for packet capture: " << errbuf << std::endl; pcap_freealldevs(alldevs); } // Set the packet capture callback function QMutexLocker unlock(&uiMutex); pcap_loop(handle, NUM_PACKETS_TO_CAPTURE, packet_handler, nullptr); // Close the packet capture handle pcap_close(handle); // Free the allocated memory for the interface list pcap_freealldevs(alldevs);}
void MainWindow::on_Start_Capture_pushButton_clicked() { //thread->start(); emit thread->mySignal(); //emit mySignal(); } void MainWindow::on_Stop_Capture_pushButton_clicked() { ui->Packet_textBrowser->clear(); }mythread.cpp
#include "mythread.h" mythread::mythread(QObject *parent) : QThread(parent) { } void mythread::mySlot() { } void mythread::run() { } -
I try to run task upon button press the task should run on thread so the GUI won't freeze , I have mythread class button
and 2 functions that will run in chain on after another ,
function 1 - run_packet_task() calls
function 2 - packet_handler(arg1,arg2...etc) - will update the guifunction 1 should start running when button clicked
function 2 called by function 1 & updates the guiI don't understand how to set this up
// on On clicked , run on_Start_Capture_pushButton_clicked
connect(ui->Start_Capture_pushButton,&QPushButton::clicked ,this ,&MainWindow::on_Start_Capture_pushButton_clicked);// mySignal calls run_packet_task - signal emitted, slot executed
connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task);Where should I implement signal and slot function so the gui will stay responsive
on_Start_Capture_pushButton_clicked
is implemented in mainwindow.cppwhen the button is clicked, it triggers the on_Start_Capture_pushButton_clicked slot, which starts the thread. As the thread runs, it emits the mySignal, which in turn executes the run_packet_task slot in the MainWindow class. Inside the run_packet_task slot, you can call the packet_handler function or perform any other desired operations.
here is all the files :
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMutex> #include <QString> #include "mythread.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class CaptureThread; // Forward declaration class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_Start_Capture_pushButton_clicked(); void run_packet_task(); void on_Stop_Capture_pushButton_clicked(); signals: void startCapture(); void mySignal(); private: Ui::MainWindow *ui; QMutex uiMutex; mythread *thread; //CaptureThread* captureThread; }; #endif // MAINWINDOW_Hmythread.h
#ifndef MYTHREAD_H // mythread.h #define MYTHREAD_H #include <QThread> class mythread : public QThread { Q_OBJECT public: explicit mythread(QObject *parent = nullptr); void run() override; signals: void mySignal(); public slots: void on_Start_Capture_pushButton_clicked(); void mySlot(); }; #endif // MYTHREAD_Hmain.cpp
#include "mainwindow.h" #include <QApplication> #include "mythread.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; mythread thread; w.show(); return a.exec(); }mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <iomanip> #include <qmessagebox.h> #include <iostream> #include <WinSock2.h> #include <pcap.h> #include <ws2tcpip.h> #include <iphlpapi.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") #define NUM_PACKETS_TO_CAPTURE 10 #include <sstream> #include <QMutex> #include <QThread> #include "mythread.h" QMutex uiMutex; Ui::MainWindow* ui; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ::ui = ui; ui->setupUi(this); // Create an instance of MyThread thread = new mythread(this); // on On clicked , run on_Start_Capture_pushButton_clicked connect(ui->Start_Capture_pushButton,&QPushButton::clicked ,this ,&MainWindow::on_Start_Capture_pushButton_clicked); // mySignal calls run_packet_task - signal emitted, slot executed connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task); //connect(ui->Start_Capture_pushButton, &QPushButton::clicked, this, &MainWindow::on_Start_Capture_pushButton_clicked); pcap_if_t* alldevs = nullptr; // Initialize the pointer to nullptr char errbuf[PCAP_ERRBUF_SIZE]; if (pcap_findalldevs(&alldevs, errbuf) == -1) { std::cerr << "Error finding network interfaces: " << errbuf << std::endl; //return nullptr; // Return nullptr on error } for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { // Display the adapter index and description ui->Choose_Adapter_comboBox->addItem(dev->description); // Increment the adapter index for the next adapter } } MainWindow::~MainWindow() { delete ui; } void packet_handler(u_char* user_data, const struct pcap_pkthdr* packet_header, const u_char* packet_data) { // Cast the user_data to a pointer of the desired type, if needed // Example: MyData* data = reinterpret_cast<MyData*>(user_data); // Create a stringstream to hold the packet information std::stringstream packetInfo; // Add packet information to the stringstream packetInfo << std::endl; packetInfo << "Packet Information:" << std::endl; packetInfo << "Capture Length: " << std::setw(5) << packet_header->caplen << std::endl; packetInfo << "Packet Length: " << std::setw(5) << packet_header->len << std::endl; packetInfo << "Packet Data:" << std::endl; // Print packet data as hexadecimal and ASCII int byte_count = 0; for (int i = 0; i < packet_header->caplen; i++) { packetInfo << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(packet_data[i]) << " "; byte_count++; // Add extra space after every 8 bytes for better readability if (byte_count % 8 == 0) packetInfo << " "; // Add newline after every 16 bytes and print ASCII representation if (byte_count % 16 == 0) { packetInfo << "| "; for (int j = i - 15; j <= i; j++) { if (packet_data[j] >= 32 && packet_data[j] <= 126) packetInfo << static_cast<char>(packet_data[j]); else packetInfo << "."; } packetInfo << std::endl; } } // Convert the packetInfo stringstream to a string std::string packetInfoString = packetInfo.str(); QString packetInfoQString = QString::fromStdString(packetInfoString); // Use QMutexLocker to lock the UI access while appending the text QMutexLocker locker(&uiMutex); ui->Packet_textBrowser->append(packetInfoQString); }void MainWindow::run_packet_task() { QString selectedDev_qt = ui->Choose_Adapter_comboBox->currentText(); char errbuf[PCAP_ERRBUF_SIZE]; pcap_if_t* alldevs = nullptr; pcap_if_t* selectedDev = nullptr; for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { if (selectedDev_qt == dev->description) { selectedDev = dev; break; } } pcap_t* handle = pcap_open_live(selectedDev->name, BUFSIZ, 1, 1000, errbuf); if (handle == nullptr) { std::cerr << "Error opening interface for packet capture: " << errbuf << std::endl; pcap_freealldevs(alldevs); } // Set the packet capture callback function QMutexLocker unlock(&uiMutex); pcap_loop(handle, NUM_PACKETS_TO_CAPTURE, packet_handler, nullptr); // Close the packet capture handle pcap_close(handle); // Free the allocated memory for the interface list pcap_freealldevs(alldevs);}
void MainWindow::on_Start_Capture_pushButton_clicked() { //thread->start(); emit thread->mySignal(); //emit mySignal(); } void MainWindow::on_Stop_Capture_pushButton_clicked() { ui->Packet_textBrowser->clear(); }mythread.cpp
#include "mythread.h" mythread::mythread(QObject *parent) : QThread(parent) { } void mythread::mySlot() { } void mythread::run() { }@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task);
How should this compile? Please provide a minimal, compilable example.
Apert from this - you must not access the ui from outside the main (gui) thread.
-
Created in Qt Creator 10.0.1 URL to the code Google Drive
-
I try to run task upon button press the task should run on thread so the GUI won't freeze , I have mythread class button
and 2 functions that will run in chain on after another ,
function 1 - run_packet_task() calls
function 2 - packet_handler(arg1,arg2...etc) - will update the guifunction 1 should start running when button clicked
function 2 called by function 1 & updates the guiI don't understand how to set this up
// on On clicked , run on_Start_Capture_pushButton_clicked
connect(ui->Start_Capture_pushButton,&QPushButton::clicked ,this ,&MainWindow::on_Start_Capture_pushButton_clicked);// mySignal calls run_packet_task - signal emitted, slot executed
connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task);Where should I implement signal and slot function so the gui will stay responsive
on_Start_Capture_pushButton_clicked
is implemented in mainwindow.cppwhen the button is clicked, it triggers the on_Start_Capture_pushButton_clicked slot, which starts the thread. As the thread runs, it emits the mySignal, which in turn executes the run_packet_task slot in the MainWindow class. Inside the run_packet_task slot, you can call the packet_handler function or perform any other desired operations.
here is all the files :
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMutex> #include <QString> #include "mythread.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class CaptureThread; // Forward declaration class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_Start_Capture_pushButton_clicked(); void run_packet_task(); void on_Stop_Capture_pushButton_clicked(); signals: void startCapture(); void mySignal(); private: Ui::MainWindow *ui; QMutex uiMutex; mythread *thread; //CaptureThread* captureThread; }; #endif // MAINWINDOW_Hmythread.h
#ifndef MYTHREAD_H // mythread.h #define MYTHREAD_H #include <QThread> class mythread : public QThread { Q_OBJECT public: explicit mythread(QObject *parent = nullptr); void run() override; signals: void mySignal(); public slots: void on_Start_Capture_pushButton_clicked(); void mySlot(); }; #endif // MYTHREAD_Hmain.cpp
#include "mainwindow.h" #include <QApplication> #include "mythread.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; mythread thread; w.show(); return a.exec(); }mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <iomanip> #include <qmessagebox.h> #include <iostream> #include <WinSock2.h> #include <pcap.h> #include <ws2tcpip.h> #include <iphlpapi.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") #define NUM_PACKETS_TO_CAPTURE 10 #include <sstream> #include <QMutex> #include <QThread> #include "mythread.h" QMutex uiMutex; Ui::MainWindow* ui; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ::ui = ui; ui->setupUi(this); // Create an instance of MyThread thread = new mythread(this); // on On clicked , run on_Start_Capture_pushButton_clicked connect(ui->Start_Capture_pushButton,&QPushButton::clicked ,this ,&MainWindow::on_Start_Capture_pushButton_clicked); // mySignal calls run_packet_task - signal emitted, slot executed connect(thread, &mythread::on_Start_Capture_pushButton_clicked, this, &MainWindow::run_packet_task); //connect(ui->Start_Capture_pushButton, &QPushButton::clicked, this, &MainWindow::on_Start_Capture_pushButton_clicked); pcap_if_t* alldevs = nullptr; // Initialize the pointer to nullptr char errbuf[PCAP_ERRBUF_SIZE]; if (pcap_findalldevs(&alldevs, errbuf) == -1) { std::cerr << "Error finding network interfaces: " << errbuf << std::endl; //return nullptr; // Return nullptr on error } for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { // Display the adapter index and description ui->Choose_Adapter_comboBox->addItem(dev->description); // Increment the adapter index for the next adapter } } MainWindow::~MainWindow() { delete ui; } void packet_handler(u_char* user_data, const struct pcap_pkthdr* packet_header, const u_char* packet_data) { // Cast the user_data to a pointer of the desired type, if needed // Example: MyData* data = reinterpret_cast<MyData*>(user_data); // Create a stringstream to hold the packet information std::stringstream packetInfo; // Add packet information to the stringstream packetInfo << std::endl; packetInfo << "Packet Information:" << std::endl; packetInfo << "Capture Length: " << std::setw(5) << packet_header->caplen << std::endl; packetInfo << "Packet Length: " << std::setw(5) << packet_header->len << std::endl; packetInfo << "Packet Data:" << std::endl; // Print packet data as hexadecimal and ASCII int byte_count = 0; for (int i = 0; i < packet_header->caplen; i++) { packetInfo << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(packet_data[i]) << " "; byte_count++; // Add extra space after every 8 bytes for better readability if (byte_count % 8 == 0) packetInfo << " "; // Add newline after every 16 bytes and print ASCII representation if (byte_count % 16 == 0) { packetInfo << "| "; for (int j = i - 15; j <= i; j++) { if (packet_data[j] >= 32 && packet_data[j] <= 126) packetInfo << static_cast<char>(packet_data[j]); else packetInfo << "."; } packetInfo << std::endl; } } // Convert the packetInfo stringstream to a string std::string packetInfoString = packetInfo.str(); QString packetInfoQString = QString::fromStdString(packetInfoString); // Use QMutexLocker to lock the UI access while appending the text QMutexLocker locker(&uiMutex); ui->Packet_textBrowser->append(packetInfoQString); }void MainWindow::run_packet_task() { QString selectedDev_qt = ui->Choose_Adapter_comboBox->currentText(); char errbuf[PCAP_ERRBUF_SIZE]; pcap_if_t* alldevs = nullptr; pcap_if_t* selectedDev = nullptr; for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { if (selectedDev_qt == dev->description) { selectedDev = dev; break; } } pcap_t* handle = pcap_open_live(selectedDev->name, BUFSIZ, 1, 1000, errbuf); if (handle == nullptr) { std::cerr << "Error opening interface for packet capture: " << errbuf << std::endl; pcap_freealldevs(alldevs); } // Set the packet capture callback function QMutexLocker unlock(&uiMutex); pcap_loop(handle, NUM_PACKETS_TO_CAPTURE, packet_handler, nullptr); // Close the packet capture handle pcap_close(handle); // Free the allocated memory for the interface list pcap_freealldevs(alldevs);}
void MainWindow::on_Start_Capture_pushButton_clicked() { //thread->start(); emit thread->mySignal(); //emit mySignal(); } void MainWindow::on_Stop_Capture_pushButton_clicked() { ui->Packet_textBrowser->clear(); }mythread.cpp
#include "mythread.h" mythread::mythread(QObject *parent) : QThread(parent) { } void mythread::mySlot() { } void mythread::run() { }@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
I try to run task upon button press the task should run on thread so the GUI won't freeze , I have mythread class button
and 2 functions that will run in chain on after another ,The whole thing makes no sense. Why you want to have that
on_button_clickedslot in your thread?You could put your task in a "worker" class (
QObjectand notQThread) and connect (inMainWindow) your start button to a function to start processing. In there you can emit your signals, to send results to yourMainWindowLike described here:
Maybe this helps as well:
https://doc.qt.io/qt-6/threads-technologies.html -
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
I try to run task upon button press the task should run on thread so the GUI won't freeze , I have mythread class button
and 2 functions that will run in chain on after another ,The whole thing makes no sense. Why you want to have that
on_button_clickedslot in your thread?You could put your task in a "worker" class (
QObjectand notQThread) and connect (inMainWindow) your start button to a function to start processing. In there you can emit your signals, to send results to yourMainWindowLike described here:
Maybe this helps as well:
https://doc.qt.io/qt-6/threads-technologies.html@Pl45m4 Thank you for the fast response, though i don't understand your answer except of the reference to the documentation
as far as i know
connect(object 1,signal ,object2,slot);
this part is very simple as far as I use 2 class object , or another thread i want to interact with , but , here I have the main thread which is object1 - ui in my case and button which is object2so it's get confusing to implement that ,
i need anther thread ? if so how I do that ?
what i put inside signal and where I put the signal() function ?
i understand the basics and more but I don't understand how Iimplemnt it in my case -
@Pl45m4 Thank you for the fast response, though i don't understand your answer except of the reference to the documentation
as far as i know
connect(object 1,signal ,object2,slot);
this part is very simple as far as I use 2 class object , or another thread i want to interact with , but , here I have the main thread which is object1 - ui in my case and button which is object2so it's get confusing to implement that ,
i need anther thread ? if so how I do that ?
what i put inside signal and where I put the signal() function ?
i understand the basics and more but I don't understand how Iimplemnt it in my case@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
as far as i know
connect(object 1,signal ,object2,slot);That's correct, but it's not about how you connect, you should rework your whole structure
Have a look at the table here
to find out what you really want/need.
Find out if your pcap lib (or the function you use from there) is async or not.
Do you want to click start, capture X packages and end? Then check out the "One call" rows. If you want to start it at some point and capture packages for a longer time (or permanent until you stop), then have a look at the Worker approach in the 5th row and also in the other link.
As it is right now, your pcap calls are in MainWindow and will block, even when you emit a signal from another thread to call that stuff. It's invoked by a signal across threads, but will still run in MainWindow's GUI thread and most likely block your GUI.
-
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
as far as i know
connect(object 1,signal ,object2,slot);That's correct, but it's not about how you connect, you should rework your whole structure
Have a look at the table here
to find out what you really want/need.
Find out if your pcap lib (or the function you use from there) is async or not.
Do you want to click start, capture X packages and end? Then check out the "One call" rows. If you want to start it at some point and capture packages for a longer time (or permanent until you stop), then have a look at the Worker approach in the 5th row and also in the other link.
As it is right now, your pcap calls are in MainWindow and will block, even when you emit a signal from another thread to call that stuff. It's invoked by a signal across threads, but will still run in MainWindow's GUI thread and most likely block your GUI.
Headers: mainwindow.h mythread.h network.h Sources: main.cpp mainwindow.cpp mythread.cpp network.cppi have created sepearate class for handling packets now i need to integrate it with the gui by threading
i have here few issues ,-
but i dont understand how to implement this
Creating Qthreads or using regualr threads ?
mainWindow runs on main thread so from this i understand i need to create QThread object since its QTapp
then connecting this QThread object to relvant function , what's the relvant function ?
its the start button which will start the packet_capture from the choosen device , and will update UI
connect(object_1 , signal(), object_2 , slot())
connect(ui->button, &QPushButton::clicked, myThread, &MyThread::handleButtonClicked);now the main issue is how i update the ui from the thread should use connect function from the thread ``` connect(myThread, signal_packet_handled, mainWindow::textBrowser , ? what should i use here ? )main_ui -> button clicked --> choosen_adpater_from_dropbox --> start_capture button clicked --> 1000 packets should be captured and printed to textBroswer boxi created each element by itself and this functionality works without threads how can it be done with threads so gui won't freeze
-
-
Headers: mainwindow.h mythread.h network.h Sources: main.cpp mainwindow.cpp mythread.cpp network.cppi have created sepearate class for handling packets now i need to integrate it with the gui by threading
i have here few issues ,-
but i dont understand how to implement this
Creating Qthreads or using regualr threads ?
mainWindow runs on main thread so from this i understand i need to create QThread object since its QTapp
then connecting this QThread object to relvant function , what's the relvant function ?
its the start button which will start the packet_capture from the choosen device , and will update UI
connect(object_1 , signal(), object_2 , slot())
connect(ui->button, &QPushButton::clicked, myThread, &MyThread::handleButtonClicked);now the main issue is how i update the ui from the thread should use connect function from the thread ``` connect(myThread, signal_packet_handled, mainWindow::textBrowser , ? what should i use here ? )main_ui -> button clicked --> choosen_adpater_from_dropbox --> start_capture button clicked --> 1000 packets should be captured and printed to textBroswer boxi created each element by itself and this functionality works without threads how can it be done with threads so gui won't freeze
@__d4ve__
Without going into any specifics of your particular situation. The pretty simple, broad principle is: use signals and slots to exchange data/synchronise between threads. If the UI wants a thread to do something, send a signal, accompanied by whatever minimal data required. If a thread has done something and wants the UI to update, send a signal from the thread and the UI will do the updates in its slot. Do not directly read or write UI stuff in the thread. -
-
@__d4ve__
Without going into any specifics of your particular situation. The pretty simple, broad principle is: use signals and slots to exchange data/synchronise between threads. If the UI wants a thread to do something, send a signal, accompanied by whatever minimal data required. If a thread has done something and wants the UI to update, send a signal from the thread and the UI will do the updates in its slot. Do not directly read or write UI stuff in the thread.@JonB
I understand how it works but having troubles accomplish it in my prog , mostly because this function :pcap_loop(handle, NUM_PACKETS_TO_CAPTURE, packet_handler, nullptr);the following function loops through packet_handler and should print packets into textBrowser but i shouldn't involve any UI updates in external functions so the question is here how it supposed to work
signal() , slot() the signal should be placed in the packet_handler function ? if so how should the connect function should be ?
connect(object1 , signal() , object2 , slot() ) connect(thread, packet_handler_signal(&packet_info) , ui->txtBrowser, append_txtBrowserFunction() ) ? -
@JonB
I understand how it works but having troubles accomplish it in my prog , mostly because this function :pcap_loop(handle, NUM_PACKETS_TO_CAPTURE, packet_handler, nullptr);the following function loops through packet_handler and should print packets into textBrowser but i shouldn't involve any UI updates in external functions so the question is here how it supposed to work
signal() , slot() the signal should be placed in the packet_handler function ? if so how should the connect function should be ?
connect(object1 , signal() , object2 , slot() ) connect(thread, packet_handler_signal(&packet_info) , ui->txtBrowser, append_txtBrowserFunction() ) ?The struct pcap_pkthdr and the packet data are not to be freed by the callback routine, and are not guaranteed to be valid after the callback routine returns; if the code needs them to be valid after the callback, it must make a copy of them.
So the first thing to do is to create a QByteArray with the data and emit the signal with it as a parameter.
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
ui->txtBrowser,
Usely the receiver is the top level object (window?)
So the signal should look like:dataReceived(const QByteArray data)and in the slot:
MyWindow::appendData(const QByteArray data) { QString str=QString::fromLocal8bit(data); // or other conversions ui->textBrowser->append(str); } -
The struct pcap_pkthdr and the packet data are not to be freed by the callback routine, and are not guaranteed to be valid after the callback routine returns; if the code needs them to be valid after the callback, it must make a copy of them.
So the first thing to do is to create a QByteArray with the data and emit the signal with it as a parameter.
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
ui->txtBrowser,
Usely the receiver is the top level object (window?)
So the signal should look like:dataReceived(const QByteArray data)and in the slot:
MyWindow::appendData(const QByteArray data) { QString str=QString::fromLocal8bit(data); // or other conversions ui->textBrowser->append(str); }@mpergand
why I need byte array ?
I added a new version of the code
hope some of you could assist with itCan't put the pieces together ...
Button Clicked -> new thread created ( captures packets ) -> delivers it to -> UI -> ui.textbrowser get updated and so on in loop until packet limit exceeded ( about 1000 packets )
( everything working without threads but ui freezes ) -
I have came up with this flow
-
We have UI main thread
we choose interface -> interface assigned to selected variable -
Start_button_clicked() -> Qt_thread created
-
Qt_thread -> runs packet_handling
-
Packet_handling -> update_ui(&Packet_info)
- Packet_info should be Locked under QMutex after sent to update_ui and Unlocked after UI updated
-
-
I have came up with this flow
-
We have UI main thread
we choose interface -> interface assigned to selected variable -
Start_button_clicked() -> Qt_thread created
-
Qt_thread -> runs packet_handling
-
Packet_handling -> update_ui(&Packet_info)
- Packet_info should be Locked under QMutex after sent to update_ui and Unlocked after UI updated
the premiss of this examples are to call a long computation (fibonacci in this case) up on a button press and displaying the result on screen, without freeze. So exactly your situation
https://github.com/DeiVadder/QtThreadExample
in particular I would suggest
WorkerObjectsubproject
https://github.com/DeiVadder/QtThreadExample/tree/master/projects/WorkerObject -
-
the premiss of this examples are to call a long computation (fibonacci in this case) up on a button press and displaying the result on screen, without freeze. So exactly your situation
https://github.com/DeiVadder/QtThreadExample
in particular I would suggest
WorkerObjectsubproject
https://github.com/DeiVadder/QtThreadExample/tree/master/projects/WorkerObject@J-Hilk This examples helped me alot to understand the concept , but the app still won't compile because of 3 errors , I updated the code and linked it
-
@J-Hilk This examples helped me alot to understand the concept , but the app still won't compile because of 3 errors , I updated the code and linked it
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
but the app still won't compile because of 3 errors ,
What errors ?
No one will read the all code to find out !Anyway, i anticiped, you will fail by emiting a signal from your static callback :)
You need to pass the address of your workObject as user param here:
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, *u_char user)
and make a cast in the callback:WorkerObject* wo=qobject_cast<WorkerObject*>(user);if it doesn't work, please post code and error texts here.
-
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
but the app still won't compile because of 3 errors ,
What errors ?
No one will read the all code to find out !Anyway, i anticiped, you will fail by emiting a signal from your static callback :)
You need to pass the address of your workObject as user param here:
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, *u_char user)
and make a cast in the callback:WorkerObject* wo=qobject_cast<WorkerObject*>(user);if it doesn't work, please post code and error texts here.
@mpergand That's the error I get :
C:\Users\Dave\Desktop\C++ Exercises\Qt Creator Projects\Project 1 - Network Packet GUI\Packet_Capture_GUI\workerobject.cpp:54: error: C2352: 'workerobject::update_gui': a call of a non-static member function requires an object ..\Packet_Capture_GUI\workerobject.cpp(54): error C2352: 'workerobject::update_gui': a call of a non-static member function requires an object C:\Users\Dave\Desktop\C++ Exercises\Qt Creator Projects\Project 1 - Network Packet GUI\Packet_Capture_GUI\workerobject.h(29): note: see declaration of 'workerobject::update_gui'Here is my code for packet handling :
void MainWindow::Packet_loop() { workerobject* worker = new workerobject(); QThread *thread_01 = new QThread(this); worker->moveToThread(thread_01); thread_01->start(); connect(thread_01 ,&QThread::started,worker, &workerobject::packet_looper); connect(worker, &workerobject::update_gui, this, &MainWindow::Update_textBrowser); } void MainWindow::Update_textBrowser(QString packetInfoQString) { ui->Packet_textBrowser->append(packetInfoQString); }signal declared this way :
signals: void update_gui(QString packetInfoQString);that's how packet handling declaration looks like :
public slots: void Update_textBrowser(QString packetInfoQString); // void Packet_loop(); private slots: void packet_handler(uchar* user_data, const struct pcap_pkthdr* packet_header, const uchar* packet_data); -
@mpergand That's the error I get :
C:\Users\Dave\Desktop\C++ Exercises\Qt Creator Projects\Project 1 - Network Packet GUI\Packet_Capture_GUI\workerobject.cpp:54: error: C2352: 'workerobject::update_gui': a call of a non-static member function requires an object ..\Packet_Capture_GUI\workerobject.cpp(54): error C2352: 'workerobject::update_gui': a call of a non-static member function requires an object C:\Users\Dave\Desktop\C++ Exercises\Qt Creator Projects\Project 1 - Network Packet GUI\Packet_Capture_GUI\workerobject.h(29): note: see declaration of 'workerobject::update_gui'Here is my code for packet handling :
void MainWindow::Packet_loop() { workerobject* worker = new workerobject(); QThread *thread_01 = new QThread(this); worker->moveToThread(thread_01); thread_01->start(); connect(thread_01 ,&QThread::started,worker, &workerobject::packet_looper); connect(worker, &workerobject::update_gui, this, &MainWindow::Update_textBrowser); } void MainWindow::Update_textBrowser(QString packetInfoQString) { ui->Packet_textBrowser->append(packetInfoQString); }signal declared this way :
signals: void update_gui(QString packetInfoQString);that's how packet handling declaration looks like :
public slots: void Update_textBrowser(QString packetInfoQString); // void Packet_loop(); private slots: void packet_handler(uchar* user_data, const struct pcap_pkthdr* packet_header, const uchar* packet_data); -
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
'workerobject::update_gui': a call of a non-static member function requires an object
Exactly what I said in my previous post.
@mpergand I didn't understand your answer currently
the function looks like this :pcap_loop(handle ,NUM_PACKETS_TO_CAPTURE ,packet_handler , nullptr);try download the code i uploaded and run it in Qt Creator
if needed I can give you writing permissions to edit the codeI don't understand how to implement the solution you proposing
-
@mpergand I didn't understand your answer currently
the function looks like this :pcap_loop(handle ,NUM_PACKETS_TO_CAPTURE ,packet_handler , nullptr);try download the code i uploaded and run it in Qt Creator
if needed I can give you writing permissions to edit the codeI don't understand how to implement the solution you proposing
-
@__d4ve__ said in Using QThread , Signal() , Slot() So GUI won't freeze:
pcap_loop(handle ,NUM_PACKETS_TO_CAPTURE ,packet_handler , nullptr);
pass this, instead of nullptr.