how to run two threads simultaneously without waiting for another thread in qt
-
In Qt,i need to call two threads in parallel without waiting for input to another thread.In my Qt application i have 2 threads,Thread1 will update status bar GUI(datetime and battery status) every 1 minute,Thread2 will call a function which will process keypressed event.I called like this in main.cpp,
StatusThread *wrkthread = new StatusThread(); QObject::connect(wrkthread, SIGNAL(requestTASKUpdate()), w, SLOT(setTaskBar())); wrkthread->start(); METhread *methread = new METhread(); QObject::connect(methread, SIGNAL(requestKBDUpdate()), w, SLOT(MainEntry())); methread->start();
both the Threads are started fine but METhread is waiting for input so StatusThread is interrupted and it continues(updating statusbar GUI every 1 minute) only after Giving input(any keypress) to METhread. What i want exactly is both the threads should run separately
-
Hi,
You should also share your threads implementation.
By the way, why use a thread to process key pressed events ? Qt already offers several possibilities to do that without adding threads.
-
Hi @AnnieAlbert
Both threads depend on the GUI (= main) thread's event loop, the first to write messages to the status bar, the second to process keyboard input. But waiting (on keyboard input) is a blocking operation. This means the status thread is indirectly also blocked.
-Michael.
-
@SGaist i am creating Qt Application for Embedded device(linux) already we have method to access keypress event in library so i called used the existing method in Qt inside thread,I attached my code please gothrough,
mainwindow.cpp
//StatusThreadvoid StatusThread::run() { emit requestTASKUpdate(); }
void MainWindow::setTaskBar() { qDebug() << "inside Thread1 setTaskBar()"; timer = new QTimer(); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(checkBatteryLevel())); timer->start(30000); } void MainWindow::checkBatteryLevel() { qDebug() << "Timer function checkBatteryLevel() is called..."; //Set DateTime QDateTime date = QDateTime::currentDateTime(); QString currentTime=date.toString("hh:mm A dd-MMM-yyyy"); datelabel->setText(currentTime); datelabel->setStyleSheet("QLabel { color : white; }"); statusBar()->addPermanentWidget(datelabel); statusBar()->setStyleSheet("background-color: rgb(0,0,0);"); QFile file("/home/btscheck.txt"); if(!file.open(QFile::WriteOnly)) { QMessageBox::information(0, "BatteryStatusCheck FileError :", file.errorString()); } QTextStream in(&file); in << "1" << endl; file.close(); qDebug() << "Timer Before 30s Delay" << endl; StatusThread::msleep(30000); //30secs qDebug() << "Timer After 30s Delay" << endl; QFile b_perFile("/home/bts.txt"); if(!b_perFile.open(QFile::ReadOnly)) { QMessageBox::information(0, "BatteryPercentage FileError", b_perFile.errorString()); } QTextStream b_per(&b_perFile); QString batteryPer = b_per.readLine(); qDebug() << "Battery Level: " << batteryPer << "%" << endl; batteryLabel->setText(batteryPer+"%"); batteryLabel->setStyleSheet("QLabel { color : white; }"); //statusBar()->addPermanentWidget(batteryLabel); b_perFile.close(); //Set Battery Icon if(batteryPer <= "20"){ batteryIcon->setPixmap(QPixmap(":/bat_20.svg").scaledToHeight(15)); qDebug() << "Battery <= 20" << endl; }else if(batteryPer > "20" && batteryPer <= "50"){ batteryIcon->setPixmap(QPixmap(":/bat_50.svg").scaledToHeight(15)); qDebug() << "Battery > 20 && <=50" << endl; }else if(batteryPer > "50" && batteryPer <= "80"){ batteryIcon->setPixmap(QPixmap(":/bat_80.svg").scaledToHeight(15)); qDebug() << "Battery > 50 && <=80" << endl; }else if(batteryPer > "80"){ batteryIcon->setPixmap(QPixmap(":/bat_full.svg").scaledToHeight(15)); qDebug() << "Battery > 80" << endl; } statusBar()->addPermanentWidget(batteryIcon); statusBar()->addPermanentWidget(batteryLabel); }
//METhread
void METhread::run() { emit requestKBDUpdate(); }
void MainWindow::MainEntry() { qDebug() << "inside Thread2 MainEntry()"; if (!lcd_getch()) { qCritical() << "Failed to create input device"; } }
bool MainWindow::lcd_getch() { struct input_event ev; struct parse_key *p; unsigned char chr_val; int codes; fd=0; qDebug() << "inside lcd_getch()"; //fd = open("kb_desc", O_WRONLY | O_NONBLOCK); fd = open(kb_desc, O_RDONLY); if (!fd) { qWarning() << "Failed to open kb"; return false; } qWarning() << "opened 'sands kbd dev'"; while (1) { read(fd, &ev, sizeof(struct input_event)); //qWarning() << "ev.type %i\n" << ev.type << endl; if(ev.type == 1) { printf("key %i state %i\n", ev.code, ev.value); //qWarning() << "key %i state %i\n" << ev.code << ev.value << endl; if(ev.value == 0) { codes = ev.code; printf("scancode: %i\n", codes); //qWarning() << "scancode: \n" << codes << endl; if((codes==58)&&(caps_lk==0)) { caps_lk = 1; } else if ((codes==58)&&(caps_lk==1)) { caps_lk = 0; } if(codes==54){shift_lk = 1;} for(p = keynames; p->name != NULL; p++) { if((codes>=16&&codes<=25)||(codes>=30&&codes<=38)||(codes>=44&&codes<=50)) { if ((p->value == (unsigned) codes)&&(p->shift == shift_lk)&&(p->caps == caps_lk)) { shift_lk = 1;shift_lk = 0; //printf("scancode: %c %i\n", p->name, codes); chr_val = p->name; printf("charval: %c \n",chr_val); //qDebug()<< "You Pressed : " + chr_val; const QString c = (const QString)chr_val; QMessageBox::information(0,"Welcome","You Pressed : " +c); //close(fd); fd=0; return chr_val; } //if } else { if ((p->value == (unsigned) codes)&&(p->shift == shift_lk)&&(p->caps == 0)) { shift_lk = 1;shift_lk = 0; //printf("scancode: %c %i\n", p->name, codes); chr_val = p->name; printf("charval-els: %c \n",chr_val); // const QString c = (const QString)chr_val; QMessageBox::information(0,"Welcome","You Pressed : " +c); // close(fd); fd=0; return chr_val; } //if } } //for } //(ev.value == 0) } //(ev.type == 1) }//while return true; }
-
To put it bluntly: you don't have threads. Or more precisely your StatusThread dies after setTaskBar has been called and because you setup a QTimer that part will work, at least partially because you're making the main thread sleep for a long time so that won't make your GUI smooth.
You should implement a real worker object doing the checkBatteryLevel work and have that object emit new data for the MainWindow to consume.
As for your second thread it's even worse, you're creating an infinite blocking loop that will block the Qt event loop so there's nothing that can be done. And again, I don't see any reason for handling keyboard like that. Qt already provides all the infrastructure you need.
Threading is a pretty tricky subject where it is very easy to shoot yourself in the foot in ways don't even realise your foot is gone and most of the leg with it. I'd recommend taking the time to read the QThread documentation and examples before going further.
-
@SGaist how to identify that StatusThread dies after setTaskBar has been called,please suggest , Is using Timer a good option to update GUI every 1 minute?
Now as you said i called KeyPress event as a Qt function by subclassing QWSKeyboardHandler and called StatusThread alone inside Thread, But still StatusThread is blocked when keypress event is running or keypress event is blocked when StatusThread is running. I have attached my modified code, please suggest calling thread and keypress event is correct or not.mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QProcess> #include <QLabel> #include <QThread> #include <QWSKeyboardHandler> #include <QKbdDriverPlugin> #include <QSocketNotifier> #include <QObject> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow, public QWSKeyboardHandler { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0,const QString &device = QString("/dev/input/by-path/platform-600000.ohci-usb-0:2:1.0-event-kbd")); ~MainWindow(); private: QSocketNotifier *m_notify; int kbdFd; bool shift; public Q_SLOTS: bool lcd_getch(); public slots: void setTaskBar(); void checkBatteryLevel(); public: void checkBatteryLevel_ini(); QProcess *process; Ui::MainWindow *ui; QTimer *timer; QLabel *batteryIcon,*datelabel,*batteryLabel; QLabel *lbl_submnu1,*lbl_submnu2,*lbl_submnu3; }; class StatusThread : public QThread{ Q_OBJECT signals: void requestTASKUpdate(); public: //static void usleep(unsigned long usecs){QThread::usleep(usecs);} //microsecs //static void sleep(unsigned long secs){QThread::sleep(secs);} //secs static void msleep(unsigned long msecs){QThread::msleep(msecs);} //millisecs private: void run(); }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "keyval.h" #include <QTextStream> #include <QByteArray> #include <QProcess> #include <QDebug> #include <QIODevice> #include <iostream> #include <QString> #include <string> #include <QTextStream> #include <stdio.h> #include <QDateTime> #include <QMessageBox> #include <QFile> #include <QLabel> #include <QTimer> #include <QMenu> #include <QMenuBar> #include <QToolBar> #include <QShortcut> #include <QDebug> #include <fcntl.h> #include <linux/input.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <stdlib.h> #include <termios.h> int caps_lk=0; int shift_lk=0; int capslock=0; int shiftlock=0; char *kb_desc = "/dev/input/by-path/platform-600000.ohci-usb-0:2:1.0-event-kbd"; int fd; MainWindow::MainWindow(QWidget *parent, const QString &device) : QMainWindow(parent), ui(new Ui::MainWindow) { process = new QProcess(this); ui->setupUi(this); batteryIcon = new QLabel(); datelabel = new QLabel(); batteryLabel = new QLabel(); qDebug() << "Loaded Example keyboard plugin"; setObjectName("Example Keypad Handler"); kbdFd = ::open(device.toLocal8Bit().constData(), O_RDONLY, 0); qDebug() << "kbdFd: " << kbdFd; if (kbdFd >= 0) { qDebug() << "Opened" << device << "as keyboard input"; m_notify = new QSocketNotifier(kbdFd, QSocketNotifier::Read, this); connect(m_notify, SIGNAL(activated(int)), this, SLOT(lcd_getch())); } else { qDebug() <<"Cannot open %s for keyboard input (%s)" << device.toLocal8Bit().constData(); return; } shift = false; } MainWindow::~MainWindow() { delete ui; if (kbdFd >= 0){ ::close(kbdFd); } } void StatusThread::run() { emit requestTASKUpdate(); } void MainWindow::setTaskBar() { qDebug() << "inside Thread1 setTaskBar()"; timer = new QTimer(); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(checkBatteryLevel())); timer->start(10000); } void MainWindow::checkBatteryLevel() { qDebug() << "Timer function checkBatteryLevel() is called..."; //Set DateTime QDateTime date = QDateTime::currentDateTime(); QString currentTime=date.toString("hh:mm A dd-MMM-yyyy"); datelabel->setText(currentTime); datelabel->setStyleSheet("QLabel { color : white; }"); statusBar()->addPermanentWidget(datelabel); statusBar()->setStyleSheet("background-color: rgb(0,0,0);"); QFile file("/home/btscheck.txt"); if(!file.open(QFile::WriteOnly)) { QMessageBox::information(0, "BatteryStatusCheck FileError :", file.errorString()); } QTextStream in(&file); in << "1" << endl; file.close(); qDebug() << "Timer Before 30s Delay" << endl; StatusThread::msleep(30000); //30secs qDebug() << "Timer After 30s Delay" << endl; QFile b_perFile("/home/bts.txt"); if(!b_perFile.open(QFile::ReadOnly)) { QMessageBox::information(0, "BatteryPercentage FileError", b_perFile.errorString()); } QTextStream b_per(&b_perFile); QString batteryPer = b_per.readLine(); qDebug() << "Battery Level: " << batteryPer << "%" << endl; batteryLabel->setText(batteryPer+"%"); batteryLabel->setStyleSheet("QLabel { color : white; }"); //statusBar()->addPermanentWidget(batteryLabel); b_perFile.close(); //Set Battery Icon if(batteryPer <= "20"){ batteryIcon->setPixmap(QPixmap(":/bat_20.svg").scaledToHeight(15)); qDebug() << "Battery <= 20" << endl; }else if(batteryPer > "20" && batteryPer <= "50"){ batteryIcon->setPixmap(QPixmap(":/bat_50.svg").scaledToHeight(15)); qDebug() << "Battery > 20 && <=50" << endl; }else if(batteryPer > "50" && batteryPer <= "80"){ batteryIcon->setPixmap(QPixmap(":/bat_80.svg").scaledToHeight(15)); qDebug() << "Battery > 50 && <=80" << endl; }else if(batteryPer > "80"){ batteryIcon->setPixmap(QPixmap(":/bat_full.svg").scaledToHeight(15)); qDebug() << "Battery > 80" << endl; } statusBar()->addPermanentWidget(batteryIcon); statusBar()->addPermanentWidget(batteryLabel); } void MainWindow::checkBatteryLevel_ini() { qDebug() << "Initial Cal--> checkBatteryLevel_ini() is called..."; //Set DateTime QDateTime date = QDateTime::currentDateTime(); QString currentTime=date.toString("hh:mm A dd-MMM-yyyy"); datelabel->setText(currentTime); datelabel->setStyleSheet("QLabel { color : white; }"); statusBar()->addPermanentWidget(datelabel); statusBar()->setStyleSheet("background-color: rgb(0,0,0);"); QFile file("/home/btscheck.txt"); if(!file.open(QFile::WriteOnly)) { QMessageBox::information(0, "BatteryStatusCheck FileError :", file.errorString()); } QTextStream in(&file); in << "1" << endl; file.close(); QFile b_perFile("/home/bts.txt"); if(!b_perFile.open(QFile::ReadOnly)) { QMessageBox::information(0, "BatteryPercentage FileError", b_perFile.errorString()); } QTextStream b_per(&b_perFile); QString batteryPer = b_per.readLine(); qDebug() << "Battery Level: " << batteryPer << "%" << endl; batteryLabel->setText(batteryPer+"%"); batteryLabel->setStyleSheet("QLabel { color : white; }"); //statusBar()->addPermanentWidget(batteryLabel); b_perFile.close(); //Set Battery Icon if(batteryPer <= "20"){ batteryIcon->setPixmap(QPixmap(":/bat_20.svg").scaledToHeight(15)); qDebug() << "Battery <= 20" << endl; }else if(batteryPer > "20" && batteryPer <= "50"){ batteryIcon->setPixmap(QPixmap(":/bat_50.svg").scaledToHeight(15)); qDebug() << "Battery > 20 && <=50" << endl; }else if(batteryPer > "50" && batteryPer <= "80"){ batteryIcon->setPixmap(QPixmap(":/bat_80.svg").scaledToHeight(15)); qDebug() << "Battery > 50 && <=80" << endl; }else if(batteryPer > "80"){ batteryIcon->setPixmap(QPixmap(":/bat_full.svg").scaledToHeight(15)); qDebug() << "Battery > 80" << endl; } statusBar()->addPermanentWidget(batteryIcon); statusBar()->addPermanentWidget(batteryLabel); } bool MainWindow::lcd_getch() { struct input_event ev; struct parse_key *p; unsigned char chr_val; int codes; fd=0; qDebug() << "inside lcd_getch()"; //fd = open("kb_desc", O_WRONLY | O_NONBLOCK); fd = open(kb_desc, O_RDONLY); if (!fd) { qWarning() << "Failed to open kb"; return false; } qWarning() << "opened 'sands kbd dev'"; while (1) { read(fd, &ev, sizeof(struct input_event)); //qWarning() << "ev.type %i\n" << ev.type << endl; if(ev.type == 1) { printf("key %i state %i\n", ev.code, ev.value); //qWarning() << "key %i state %i\n" << ev.code << ev.value << endl; if(ev.value == 0) { codes = ev.code; printf("scancode: %i\n", codes); //qWarning() << "scancode: \n" << codes << endl; if((codes==58)&&(caps_lk==0)) { caps_lk = 1; } else if ((codes==58)&&(caps_lk==1)) { caps_lk = 0; } if(codes==54){shift_lk = 1;} for(p = keynames; p->name != NULL; p++) { if((codes>=16&&codes<=25)||(codes>=30&&codes<=38)||(codes>=44&&codes<=50)) { if ((p->value == (unsigned) codes)&&(p->shift == shift_lk)&&(p->caps == caps_lk)) { shift_lk = 1;shift_lk = 0; //printf("scancode: %c %i\n", p->name, codes); chr_val = p->name; printf("charval: %c \n",chr_val); //qDebug()<< "You Pressed : " + chr_val; const QString c = (const QString)chr_val; QMessageBox::information(0,"Welcome","You Pressed : " +c); //close(fd); fd=0; return chr_val; } //if } else { if ((p->value == (unsigned) codes)&&(p->shift == shift_lk)&&(p->caps == 0)) { shift_lk = 1;shift_lk = 0; //printf("scancode: %c %i\n", p->name, codes); chr_val = p->name; printf("charval-els: %c \n",chr_val); // const QString c = (const QString)chr_val; QMessageBox::information(0,"Welcome","You Pressed : " +c); //close(fd); fd=0; return chr_val; } //if } } //for } //(ev.value == 0) } //(ev.type == 1) }//while return true; }
main.cpp
int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow *w = new MainWindow(); w->setWindowFlags(Qt::FramelessWindowHint); w->show(); w->checkBatteryLevel_ini(); StatusThread *wrkthread = new StatusThread(); QObject::connect(wrkthread, SIGNAL(requestTASKUpdate()), w, SLOT(setTaskBar())); wrkthread->start(); return a.exec(); }
-
The
run
function is called in result to the call to QThread'sstart
. Your run method does only one thing: emit a signal. Therefore, as soon as the signal is emitted, the method ends and so does your thread.Since you want to check the battery status every 30 seconds, why not just have a timer call your battery checking slot every 30 seconds ?
There's also no need to set the style sheet again and again since it never changes.
As for the keyboard, why not just use the keyPressEvent method to handle your keyboard related events ?
-
@SGaist keyPressEvent works fine in Desktop alone, But we are developing Qt Application for Linux Embedded Device( followed the [link] )(https://radekp.github.io/qtmoko/api/tut-deviceexample.html) so only i used QWSKeyBoardHandler , As you said, now i removed Thread and called checkBatteryLevel() inside Timer but still the same problem persists. The change i did is called setTaskBar() directly in main.cpp as
w->setTaskBar();
please suggest what change should be done in my code to run both parallely
-
Stop sleeping during your battery check slot. If you really want the second part to happen 30 second after the first one, move the code of the second part to its own slot and use a single shot timer that you start after after you're done with the the first part.
As for the keyboard, the first question is: do you really need to implement a custom keyboard handler ? Is your keyboard not already supported by the system and thus the event sent to Qt ?
-
You're welcome !
Since you have it working now, please mark that thread as solved using the "Topic Tools" button so that other forum users may know a solution has been found :)