Want to update a QtableWidget within a QThread - c++

I'm starting a project called Nice System Monitor aiming to monitor processes on Linux, and I'm using C++ and Qt with QtCreator.
I've started making a QThread with a function called to fill a QTableWidget repeatedly but the table doesn't update properly even if I delete each row before fulling it up again.
I'm quite new to Qt and inspired myself of different sources on the Internet.
Here's the code of the QThread :
#include <unistd.h>
#include <ios>
#include <iostream>
#include <fstream>
#include <string>
#include <dirent.h>
#include <stdio.h>
#include <cctype>
#include <stdlib.h>
#include <vector>
#include <sstream>
#include "renderprocesstablethread.h"
#include <proc/readproc.h>
#include <proc/procps.h>
#include "mainwindow.h"
using namespace std;
RenderProcessTableThread::RenderProcessTableThread(QObject *parent)
: QThread(parent)
{
restart = false;
abort = false;
}
RenderProcessTableThread::~RenderProcessTableThread()
{
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();
wait();
}
bool RenderProcessTableThread::isNum(char *s) {
int i = 0, flag;
while(s[i]){
//if there is a letter in a string then string is not a number
if(isalpha(s[i]) || s[i] == '.'){
flag = 0;
break;
}
else flag = 1;
i++;
}
if (flag == 1) return true;
else return false;
}
string RenderProcessTableThread::convertDouble(double value) {
std::ostringstream o;
if (!(o << value))
return "";
return o.str();
}
string RenderProcessTableThread::convertInt(int value) {
std::ostringstream o;
if (!(o << value))
return "";
return o.str();
}
void RenderProcessTableThread::run()
{
forever {
mutex.lock();
mutex.unlock();
fillProcessTable();
sleep(1000);
//cout << "ça marche" << endl;
}
mutex.lock();
if (!restart)
condition.wait(&mutex);
restart = false;
mutex.unlock();
}
void RenderProcessTableThread::setLocalMainWindow(MainWindow& w)
{
localMainWindow = &w;
ui_tableWidgetProcessus = localMainWindow->findChild<QTableWidget*>("tableWidgetProcessus");
ui_tableWidgetProcessus->setColumnCount(11);
ui_tableWidgetProcessus->setColumnWidth(10,508);
QFont fnt;
fnt.setPointSize(10);
fnt.setFamily("Arial");
ui_tableWidgetProcessus->setFont(fnt);
QStringList labels;
labels << "user" << "pid" << "cpu" << "nice" << "vsz" << "rss" << "tty" << "stat" << "start" << "time" << "cmd";
ui_tableWidgetProcessus->setHorizontalHeaderLabels(labels);
}
void RenderProcessTableThread::fillProcessTable() {
QMutexLocker locker(&mutex);
if (!isRunning()) {
start(LowPriority);
} else {
restart = true;
condition.wakeOne();
}
PROCTAB* proc = openproc(PROC_FILLUSR | PROC_FILLMEM | PROC_FILLSTAT | PROC_FILLSTATUS | PROC_FILLARG);
proc_t proc_info;
memset(&proc_info, 0, sizeof(proc_info));
int totalRow = ui_tableWidgetProcessus->rowCount();
for ( int i = 0; i < totalRow ; ++i )
{
ui_tableWidgetProcessus->removeRow(i);
}
int i = 0;
while (readproc(proc, &proc_info) != NULL) {
cout << proc_info.fuser << proc_info.tid << proc_info.cmd << proc_info.resident << proc_info.utime << proc_info.stime << endl;
ui_tableWidgetProcessus->setRowCount(i+1);
ui_tableWidgetProcessus->setItem(i,0,new QTableWidgetItem(QString(proc_info.fuser),0));
ui_tableWidgetProcessus->setItem(i,1,new QTableWidgetItem(QString((char*)convertInt(proc_info.tid).c_str()),0));
ui_tableWidgetProcessus->setItem(i,2,new QTableWidgetItem(QString((char*)convertInt(proc_info.pcpu).c_str()),0));
ui_tableWidgetProcessus->setItem(i,3,new QTableWidgetItem(QString((char*)convertInt(proc_info.nice).c_str()),0));
ui_tableWidgetProcessus->setItem(i,4,new QTableWidgetItem(QString((char*)convertInt(proc_info.vm_size).c_str()),0));
ui_tableWidgetProcessus->setItem(i,5,new QTableWidgetItem(QString((char*)convertInt(proc_info.rss).c_str()),0));
ui_tableWidgetProcessus->setItem(i,6,new QTableWidgetItem(QString((char*)convertInt(proc_info.tty).c_str()),0));
ui_tableWidgetProcessus->setItem(i,7,new QTableWidgetItem(QString(proc_info.state),0));
ui_tableWidgetProcessus->setItem(i,8,new QTableWidgetItem(QString((char*)convertInt(proc_info.start_time).c_str()),0));
ui_tableWidgetProcessus->setItem(i,9,new QTableWidgetItem(QString((char*)convertInt(proc_info.stime).c_str()),0));
//cout << "proc_info.tid : " << proc_info.tid << endl;
//cout << "proc_info.cmdline : " << proc_info.cmdline << endl;
string text;
if (proc_info.cmdline != 0) {
vector<string> v(proc_info.cmdline, proc_info.cmdline + sizeof(proc_info.cmdline) / sizeof(string));
text = v[0];
}
else {
vector<string> v;
v.push_back(proc_info.cmd);
text = v[0];
}
//string text = char_to_string(proc_info.cmdline);
ui_tableWidgetProcessus->setItem(i,10,new QTableWidgetItem(QString((char*)text.c_str()),0));
i++;
}
closeproc(proc);
}
Are they better ways of doing this ?
Thanks
Patrick

This looks like something for Qt's Signal and Slots.
In your case the the thread emits the signal and a slot in your window will be called.
So in your RenderProcessTableThread.h define a signal
signals:
void newValues(const QString &data);
And in your mainwindow.h
public slots:
void showNewValues(const QString &data);
add the data to your table in this slot.
Then you have to connect them (e. g. in the constructor of your mainwindow after the creation of the thread)
connect(yourThread, SIGNAL(newValues(QString)), this, SLOT(showNewValues(QString)));
Whenever you want to show new data, emit the signal (e. g. somewhere in your fillProcessTable() function):
emit newValues(yourValues);
Qt does the connection between the threads for you.

Related

How to get a generic callback function or method with any types of return or parameters?

I'm making some API and want to allow app developers to put their own callback function with any return types and any number of parameters. Below code is example I expect to work. I wonder how to define ?<?> userfunc in MyAPI.h
// MyAPI.h
using namespace API {
/**
* Executes callback function after a random time in its own thread.
* #param userfunc User defined callback function.
**/
void SetCallback(?<?> userfunc);
}
// application.cpp
#include "MyAPI.h"
#include <chrono>
#include <format>
#include <iostream>
std::string get_current_datetime() {
const auto now = std::chrono::system_clock::now();
return std::format("{:%d-%m-%Y %H:%M:%OS}", now);
}
bool callback_1(bool* triggered, std::string* registration_time) {
*triggered = true;
std::cout << "CALLBACK_1" << std::endl;
std::cout << "registered: " << registration_time << std::endl;
std::Cout << "triggered : " << get_current_datetime() << std::endl;
return true;
}
int callback_2(int* triggered) {
*triggered = 1;
std::cout << "CALLBACK_2" << std::endl;
return 1;
}
int main(void) {
bool is_triggered_bool = false;
int is_triggered_int = 0;
std::string curr = get_current_datetime();
API::SetCallback(std::bind(&callback_1, &is_triggered_bool, &curr));
API::SetCallback(std::bind(&callback_2, &is_triggered_int))
Sleep(3000);
if (is_triggered_bool && is_triggered_int == 1) return 0;
else return -1;
}
Any advice will appreciate

How to change this notify_one so that it chooses a random thread?

Thanks in advance for any help.
Trying to make a program that would create 6 threads, then each 2 seconds randomly choose one and make it print its number. I am obviously doing something wrong, because it just keeps printing 0-1-2-3-4-5 endlessly. The code is below.
Main question is, what should i do to make random threads unlock?
#include <thread>
#include <memory>
#include <chrono>
#include <condition_variable>
std::condition_variable* cv = new std::condition_variable();
std::mutex cv_m;
void threadFunc(std::shared_ptr<bool> flag2, int id)
{
while (true)
{
std::unique_lock<std::mutex> lock(cv_m);
cv->wait(lock);
if (true)
if (*flag2) std::cout << "Thread" << " " << id << std::endl;
}
}
int main() {
std::shared_ptr<bool> f2 = std::make_shared<bool>(false);
std::thread threads[6];
for (int i = 0; i < 6; i++)
threads[i] = std::thread(threadFunc, f2, i);
*f2 = true;
while (true)
{
cv->notify_one();
std::this_thread::sleep_for(std::chrono::seconds(2));
}
return 0;
}
You can use a condition variable for each thread, it should be false for each thread at the beginning, then change a random condition variable to true and notify all, this will make a random thread to wake up (the thread that owns that condition variable)
here is the full solution
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
#include "UserInterruptHandler.h"
using namespace std;
UserInterruptHandler h;
condition_variable conditionalVariable;
mutex mtx;
bool flag = true;
void myMethod(int id, bool *canWork) {
unique_lock<mutex> ul(mtx);
while (flag) {
conditionalVariable.wait(ul,[=]{return *canWork;});
if(!flag)
break;
cout << "thread " << id << endl;
}
cout << "thread " << id << " exits.." << endl;
}
int main() {
cout << "input thread count" << endl;
int n;
cin >> n;
thread myThreads[n];
bool *canWork = new bool[n];
for (int i = 0; i < n; i++) {
canWork[i] = false;
myThreads[i] = thread(myMethod, i + 1, &canWork[i]);
}
while (!h.checkInterruption()) {
int i = rand() % n;
canWork[i] = true;
conditionalVariable.notify_all();
canWork[i] = false;
usleep(1000);
}
flag = false;
int i = 0;
for (thread &th:myThreads) {
canWork[i++] = true;
conditionalVariable.notify_all();
if (th.joinable())
th.join();
}
}
notice that here I am using header UserInterruptHandler.h to handle CTR+C event to end all threads gracefully

C++ Boost multithreading inside a class

I am following Boost multithreading tutorial here
. Following section 18.13, I try creating a class containing multiple threads as follows:
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <iostream>
#include <string>
#include <queue>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using boost::asio::ip::udp;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using namespace std;
class MultiTask
{
private:
boost::thread_group threads; // thread group
boost::thread* thread_main; // main thread
boost::thread* thread_output; // output thread
boost::thread* thread_input; // input thread
boost::mutex stopMutex;
bool stop;
int i_in, i_out, i_main;
string userInput;
public:
// constructor
MultiTask()
{
thread_main = new boost::thread(boost::ref(*this));
thread_output = new boost::thread(&MultiTask::Callable_Out, this, 1000, boost::ref(i_out));
thread_input = new boost::thread(&MultiTask::Callable_In, this, 1000, boost::ref(i_out), boost::ref(userInput));
//threads.add_thread(thread_main); // main thread = 0 // will throw -> boost thread: trying to join itself
threads.add_thread(thread_output); // output thread = 1
threads.add_thread(thread_input); // input thread = 2
stop = false;
i_in = 0;
i_out = 0;
i_main = 0;
userInput = "";
}
// destructor
~MultiTask()
{
// stop all threads
Stop();
// show exit message
cout << "Exiting MultiTask." << endl;
}
// start the threads
void Start()
{
// Wait till they are finished
threads.join_all();
}
// stop the threads
void Stop()
{
// warning message
cout << "Stopping all threads." << endl;
// signal the threads to stop (thread-safe)
stopMutex.lock();
stop = true;
stopMutex.unlock();
// wait for the threads to finish
threads.interrupt_all();
threads.join_all();
}
void Callable_Out(int interval, int& count)
{
while (1)
{
//cout << "Callable_Out [" << count++ << "]" << endl;
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
}
}
void Callable_In(int interval, int& count, string& userInput)
{
while (1)
{
cout << "Callable_In [" << count++ << "]. Enter message: ";
getline(cin, userInput);
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
}
}
// Thread function
void operator () ()
{
while (1)
{
//cout << "Main [" << i_main++ << "]." << endl;
//cout << "Main [" << i_main++ << "]. " << userInput << endl;
if (userInput == "STOP")
{
try
{
this->Stop();
}
catch(exception e)
{
cout << e.what() << endl;
}
}
boost::this_thread::sleep(boost::posix_time::millisec(1000));
boost::this_thread::interruption_point();
}
}
};
int main()
{
MultiTask mt;
mt.Start();
}
However, VS throws two of these errors:
Severity Code Description Project File Line Suppression State
Error C2198 'void (__cdecl *)(boost::posix_time::millisec,int &,std::string &)': too few arguments for call mycpp c:\boost_1_66_0\boost\bind\bind.hpp 259
Can someone please help? This is from section 18.13. Also, I do not see where to input the arguments for CallableFunction() in that example. How can it be done in my case? Thanks.
In tutorial CallableFunction function takes only one parameter, it is passed as second parameter in thread constructor new boost::thread(&CallableFunction, i);.
In your case Callable_Out takes 2 parameters, one is missing, you should call
thread_output = new boost::thread(&Callable_Out, boost::posix_time::millisec(0), boost::ref(i_out));
and for Callable_In you call
thread_input = new boost::thread(&Callable_In, boost::posix_time::millisec(1), boost::ref(i_out), boost::ref(userInput));

Terminating while thread

I can't close my thread. Am I forgetting to do something? The thread seems like it's saving the value I'm using for close, and then never checks if it has changed. Here is some example code that has an identical effect:
#include "stdafx.h"
#include "Windows.h"
#include <iostream>
#include <thread>
class test {
private:
bool user_wants_thread = true;
bool time_to_close = false;
public:
bool set_timetoclose(bool in) {
time_to_close = in;
if (time_to_close == in) {
return true;
}
return false;
}
void function() {
while (user_wants_thread) {
// CODE
std::cout << time_to_close;
Sleep(100);
if (time_to_close) {
goto close;
}
}
close:
Sleep(1);
}
};
int main() {
test t;
std::thread thread_func(&test::function, t);
Sleep(1000);
bool success;
do {
success = t.set_timetoclose(true);
} while (!success);
thread_func.join();
std::cout << "Closed";
std::cin.get();
}
I removed some unused parts and changed the actual condition to be an atomic<bool> and it seems to work as shown on this link:
http://rextester.com/TWHK12491
I'm not claiming this is absolutely correct, however, but it shows how using the atomic causes synchronization across reads/writes to the value which could result in a data race.
#include "Windows.h"
#include <iostream>
#include <thread>
#include <atomic>
class test {
public:
std::atomic<bool> time_to_close = false;
test()=default;
void function() {
while (!time_to_close) {
std::cout << "Running..." << std::endl;
Sleep(100);
}
std::cout << "closing" << std::endl;
}
};
int main() {
test t;
std::thread thread_func([&t](){t.function();});
Sleep(500);
t.time_to_close = true;
std::cout << "Joining on thread" << std::endl;
thread_func.join();
std::cout << "Closed";
return 0;
}

Why does the console stays open although the program is finished?

I'm currently working on a web crawler with the ability to abort at any time when the 'ESC'-button is pressed.
To check whether 'ESC' is pressed, I'm using a thread which sets a flag to signal the 'main'-function to terminate itself. Although this is working fine, the console window doesn't close after the program is finished. The only circumstance under which the console window is closing, is when 'ESC' is pressed immediatly after the thread starts (so before the while loop is starting; see comment in the code).
#include <curl/curl.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <regex>
#include <queue>
#include <stdio.h>
#include <string>
#include <thread>
#include <vector>
#include <windows.h>
boolean exitCrawler{ false };
char URL[512];
CURL *curl;
int i{ 0 }, start{ 0 }, stop{ 0 }, urlCount{ 0 };
std::ofstream write;
std::deque <const char*> urls;
std::regex rgx("(?:http)[:](?://)[A-Za-z0-9]+.[A-Za-z0-9]+.[a-z]+/");
std::smatch sm;
std::string answer, content;
std::vector <std::string> detectedURLs;
unsigned short keyState;
auto analyzeContent() -> void;
auto crawlSite() -> int;
auto indexSite() -> void;
auto quitCrawler() -> void;
auto writeContent(char*, size_t, size_t)->size_t;
auto main() -> void {
SetConsoleTitle(L"WebCrawler");
write.open("C:\\Users\\Daniel\\Desktop\\urls.txt", std::ios::app);
std::cout << "Welcome to the WebCrawler!" << std::endl << "URL (http://www.*.*/): ";
std::cin >> URL;
std::cout << "Starting to crawl the surface web. Press \"ESC\" at any time to cancel the crawler." << std::endl;
urls.push_back(URL);
std::thread qC(quitCrawler);
qC.detach();
//Press 'ESC' here to get the console window to close itself after the program finishes. If you press 'ESC' after the while loop starts, the console window will stay open.
while (true) {
std::string s(urls.front(), strlen(urls.front()));
if (s.find("http://") == -1) {
urls.pop_front();
content = "";
continue;
}
if (crawlSite() == 1) {
urls.pop_front();
content = "";
continue;
}
analyzeContent();
std::cout << "\rCrawled " << urlCount << " sites of the surface web.";
urls.pop_front();
if ((urls.size() == 0) || exitCrawler) {
break;
}
}
std::cout << std::endl << "Crawler terminating..." << std::endl;
write.close();
}
auto analyzeContent() -> void {
memset(&sm, 0, sizeof(sm));
i = 0;
std::string::const_iterator beginningIterator = content.cbegin();
while (std::regex_search(beginningIterator, content.cend(), sm, rgx)) {
beginningIterator = sm[0].second;
if ((std::find(detectedURLs.begin(), detectedURLs.end(), sm[0])) == detectedURLs.end()) {
urls.push_back(sm[0].str().c_str());
i++;
}
detectedURLs.push_back(sm[0]);
}
}
auto crawlSite() -> int {
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
if (curl_easy_setopt(curl, CURLOPT_URL, urls.front()) != CURLE_OK) {
return 1;
}
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeContent);
if (curl_easy_perform(curl) != CURLE_OK) {
return 1;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
indexSite();
return 0;
}
auto indexSite() -> void {
urlCount++;
write << " #" << urlCount << "     [" << urls.front() << "]/[...] --- {[...]} --- " << std::time(0) << "<br>" << std::endl;
}
auto quitCrawler() -> void {
keyState = GetAsyncKeyState(VK_ESCAPE);
while (true) {
if (keyState != 0) {
exitCrawler = true;
break;
}
keyState = GetAsyncKeyState(VK_ESCAPE);
}
}
auto writeContent(char* buffer, size_t size, size_t nmemb) -> size_t {
content.append(buffer);
return size * nmemb;
}
To be honest, I do not have any clue why the program is staying open after the code is finished.Does anybody of you have any idea?