How to automatically update a statusbar message? - c++

I am using Qt 5.15.1 and C++ to create a simple app which processes signals from hardware and displays images and driver status. I want to update the statusbar message when an int value defined in another class changes. I would like this to happen automatically, each time this value changes. I understand that I need signals and slots to achieve this. So far I have done the following:
signalprocessing.h
class SignalProcessing: public QObject
{
Q_OBJECT
public:
SignalProcessing(QObject *parent = nullptr);
private:
int status;
public slots:
int GetStatus();
signals:
void StatusChanged();
}
signalprocessing.cpp
SignalProcessing::SignalProcessing(QObject *parent)
: QObject(parent)
{
}
int SignalProcessing::GetStatus()
{
emit(StatusChanged());
return status;
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
virtual ~MainWindow();
SignalProcessing *signalProcessing;
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
signalProcessing= new SignalProcessing(this);
ui->statusbar->showMessage(QString::number(signalProcessing->GetStatus()));
}
The problem is that the statusbar message is currently not updated automatically, but set to a given value.
How do I make sure it will always display the current status?

The idea is that you want to emit your StatusChanged signal when the value has actually changed, not when you call GetStatus(). So you need a SetStatus() function, and your SignalProcessing class will need to know when to call that. Then you want to connect that signal to a slot that then updates your status bar. It might look something like this:
class SignalProcessing: public QObject
{
Q_OBJECT
public:
SignalProcessing(QObject *parent = nullptr);
int getStatus();
void setStatus(int value);
private:
int status;
signals:
void statusChanged(); // It's better to start signals with a lower case letter
}
signalprocessing.cpp
SignalProcessing::SignalProcessing(QObject *parent)
: QObject(parent)
{
}
int SignalProcessing::getStatus()
{
return status;
}
void SignalProcessing::setStatus(int value)
{
if (status != value)
{
status = value;
emit statusChanged();
}
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
virtual ~MainWindow();
SignalProcessing *signalProcessing;
public slots:
void updateStatus();
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
signalProcessing= new SignalProcessing(this);
connect(signalProcessing, &SignalProcessing::statusChanged, this, &MainWindow::updateStatus);
updateStatus();
}
void MainWindow::updateStatus()
{
ui->statusbar->showMessage(QString::number(signalProcessing->getStatus()));
}

First change singal declartion so it will provide new value:
class SignalProcessing: public QObject
{
Q_OBJECT
public:
SignalProcessing(QObject *parent = nullptr);
private:
int status;
public slots:
int setStatus(int value);
signals:
void statusChanged(const QString& message);
}
void SignalProcessing::setStatus(int value)
{
if (status != value)
{
status = value;
emit statusChanged(tr("Status is %1").arg(status));
}
}
Then connect this signal to QStatusBar::showMessage slot and you done.

Related

How to run a QThread from QWizardPage and access field()

I need some advice to access the field(QString name) variable in QWizardPage from a QThread. I'm building some kind of an installer and I want to do the installing work in a separate Thread.
My purpose:
When reached the commit/install page, I want to execute code to do the "installing" and update the QWizardPage with my progress, until its finished.
The install function is dependent on many field() variables from other QWizardPages. Therefore I tried to execute this install function from a QThread, which is defined in an inner class from my QWizardPage. The problem is, the field()-function i a non-static member and so it's not working. And so I'm out of ideas to run my install-function parallel to my WizardPage.
I tried something like this:
InstallPage.h
class InstallPage : public QWizardPage
{
Q_OBJECT
class WorkerThread : public QThread
{
Q_OBJECT
void run() override;
};
public:
InstallPage(QWidget *parent = 0);
private:
QLabel *lProgress;
WorkerThread *installer;
void install();
};
InstallPage.c
InstallPage::InstallPage(QWidget *parent)
: QWizardPage(parent)
{
...
installer = new WorkerThread(this);
installer->start();
}
void InstallPage::WorkerThread::run()
{
if(field("checkBox1").ToBool())
{
doStuff();
}
}
//QT-Creator says at field("checkBox1"):
//error: call to non-static member function without an object argument
I'm also open for any other idea to make my installer work. Maybe someone knows something I haven't thought of.
Another approach is to create a worker (QObject) that lives in another thread that performs the heavy task and notifies the status of that task through signals:
#include <QtWidgets>
class InitialPage: public QWizardPage
{
public:
InitialPage(QWidget *parent = nullptr): QWizardPage(parent)
{
QSpinBox *spinbox = new QSpinBox;
QLineEdit *lineedit = new QLineEdit;
QVBoxLayout *lay = new QVBoxLayout(this);
lay->addWidget(spinbox);
lay->addWidget(lineedit);
registerField("value1", spinbox);
registerField("value2", lineedit);
}
};
class InstallWorker: public QObject
{
Q_OBJECT
public:
InstallWorker(QObject *parent=nullptr): QObject(parent)
{
}
public Q_SLOTS:
void install(int param1, const QString & param2)
{
Q_EMIT started();
for(int i=0; i < 100; i++){
qDebug() << __PRETTY_FUNCTION__ << i << param1 << param2;
QThread::msleep(100);
Q_EMIT progressChanged(i);
}
qDebug()<< __PRETTY_FUNCTION__ << "finished";
Q_EMIT finished();
}
Q_SIGNALS:
void started();
void progressChanged(int value);
void finished();
};
class InstallPage: public QWizardPage
{
Q_OBJECT
public:
InstallPage(QWidget *parent = nullptr): QWizardPage(parent),
label(new QLabel), progressbar(new QProgressBar)
{
QVBoxLayout *lay = new QVBoxLayout(this);
lay->addWidget(label);
lay->addWidget(progressbar);
progressbar->setMinimum(0);
progressbar->setMaximum(100);
thread = new QThread(this);
worker.moveToThread(thread);
connect(&worker, &InstallWorker::started, this, &InstallPage::onStarted);
connect(&worker, &InstallWorker::finished, this, &InstallPage::onFinished);
connect(&worker, &InstallWorker::progressChanged, this, &InstallPage::onProgressChanged);
thread->start();
}
~InstallPage(){
thread->quit();
thread->wait();
}
void initializePage(){
start_install();
}
private Q_SLOTS:
void start_install(){
int param1 = field("value1").toInt();;
QString param2 = field("value2").toString();
QMetaObject::invokeMethod(&worker, "install", Qt::QueuedConnection, Q_ARG(int, param1), Q_ARG(QString, param2));
}
void onStarted(){
for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
if(QAbstractButton * button = wizard()->button(which))
button->setEnabled(false);
}
void onFinished(){
for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
if(QAbstractButton * button = wizard()->button(which))
button->setEnabled(true);
wizard()->next();
}
void onProgressChanged(int value){
progressbar->setValue(value);
label->setNum(value);
}
private:
InstallWorker worker;
QThread *thread;
QLabel *label;
QProgressBar *progressbar;
};
class FinalPage: public QWizardPage
{
public:
FinalPage(QWidget *parent = nullptr): QWizardPage(parent)
{
}
};
#include "main.moc"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWizard wizard;
wizard.addPage(new InitialPage);
wizard.addPage(new InstallPage);
wizard.addPage(new FinalPage);
wizard.show();
return app.exec();
}

findChild never displays textBrowser

I trying to display some text to a textbrowser via: FindChild and it never displays it in the textbrowswer any help would be helpfull..
Here is my mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
namespace Ui
{
class MainWindow;
class TestWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static MainWindow* GetInstance(QWidget* parent = 0);
signals:
public slots:
void on_pushButton_3_clicked();
void MainWindow_TextBrowser_String(const QString & newText);
private:
Ui::MainWindow *ui;
static MainWindow* mainInstance;
};
Here is my mainwindow.cpp
// Constructor
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
// Destructor
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_3_clicked()
{
TestWindow mwindow;
mwindow.start();
}
MainWindow* MainWindow::mainInstance = 0;
MainWindow* MainWindow::GetInstance(QWidget *parent)
{
if (mainInstance == NULL)
{
mainInstance = new MainWindow(parent);
}
return mainInstance;
}
void MainWindow::MainWindow_TextBrowser_String(const QString & newText)
{
QString TextBrowser_String = QString(newText);
ui->textBrowser->append(TextBrowser_String);
}
I create the testwindow object in the pushbutton send the start function to call the findchild window to send a string to the textbrowser window
Here is my testwindow.cpp
void testwindow::start()
{
// Create a new mainwindow on the heap.
MainWindow* instance = MainWindow::GetInstance();
// Or I can call
// MainWindow instance; then point to findchild
QString Test_Window_String = QStringLiteral("Test Window String");
instance->findChild<QTextBrowser*>("textBrowser")->append(Test_Window_String);
}
I understand that you can use a singal and slot and simply just create a signal that sends the string to the append textbrowser
void testwindow::singalandslot()
{
MainWindow* instance = MainWindow::GetInstance();
connect(this, SIGNAL(TextBrowswer_String(const QString &)), instance , SLOT(MainWindow_TextBrowser_String(QString &)));
}
void testwindow::fireSignal()
{
emit TextBrowswer_String("sender is sending to receiver.");
}
Even with a signal or FindChild it seems that the object is already deleted or i'm doing something wrong.
Can you please share your Ui::MainWindow Class and setupUi implementation to get a clear view?
Hope you have created the instance for QTextBrowser* inside setupUi or in the constructor.
With the below setupUi implementation, both ur usecases are working.
namespace Ui
{
class MainWindow: public QWidget
{
Q_OBJECT
public:
QTextBrowser* textBrowser;
void setupUi(QWidget* parent)
{
setParent(parent);
textBrowser = new QTextBrowser(parent);
textBrowser->setObjectName("textBrowser");
textBrowser->setText("Hello");
}
};
}

Qt - signals and slots in 2 classes

I am "playing" with signals and slots in Qt.
I made 2 classes (MainWindow,DatabaseManager)
databasemanager.h
class DatabaseManager : public QObject
{
Q_OBJECT
QSqlDatabase database;
public:
DatabaseManager();
signals:
void TextSignal(const QString);
};
databasemanager.cpp
DatabaseManager::DatabaseManager()
{
database = QSqlDatabase::addDatabase("QSQLITE");
database.setDatabaseName("database.sqlite");
if( !database.open() )
qDebug() << "Cannot open connection with database";
else
{
emit TextSignal("Connected");
qDebug() << "Connected";
}
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void WriteText(const QString text);
private:
Ui::MainWindow *ui;
DatabaseManager db;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(&db,SIGNAL(TextSignal(QString)),this,SLOT(WriteText(QString)) );
}
void MainWindow::WriteText(const QString text)
{
ui->textEdit->append(text);
}
And here is my question - What am I doing wrong?
While debugging text "Connected" is printed in Debug Window but it is not showed in textEdit.
db object's constructor called before (even before mainWindow constructor and setupUi).
private:
Ui::MainWindow *ui;
DatabaseManager db;
QObject::connect(&db,SIGNAL(TextSignal(QString)),this,SLOT(WriteText(QString))
Signal was emitted before connect and before creation of ui->textEdit.
"Cherkesgiller Tural" is spot on. So to remedy your problem, change your DatabaseManager to a pointer:
DatabaseManager* db;
And instantiate it after setupUi, but before your connect.
ui->setupUi(this);
db = new DatabaseManager();
QObject::connect(db,SIGNAL(TextSignal(QString)),this,SLOT(WriteText(QString)) );

Program crashes because of wrong usage of slots and signals

What I'm trying to do is to call an time consuming operation (MockClamWrapper::loadDatabase()) in a separate thread at the moment of creation of my window and then to update my window once the operation is completed. Here is the code that I have.
MockClamWrapper.h
class MockClamWrapper : QObject
{
Q_OBJECT
public:
MockClamWrapper();
~MockClamWrapper();
bool loadDatabase(unsigned int *signatureCount=NULL);
Q_SIGNALS:
void databaseLoaded();
};
MockClamWrapper.cpp
bool MockClamWrapper::loadDatabase(unsigned int *signatureCount){
QThread::currentThread()->sleep(10);
databaseLoaded();
return true;
}
MainWindow.h
#include <QMainWindow>
#include <QFileDialog>
#include "mockclamwrapper.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void enableWindow();
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
MockClamWrapper *clam;
void initWindow();
};
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect((QObject*)clam, SIGNAL(databaseLoaded()),(QObject*)this,SLOT(enableWindow()));
QFuture<void> fut = QtConcurrent::run(this,&MainWindow::initWindow);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initWindow(){
clam->loadDatabase(NULL);
}
void MainWindow::enableWindow(){
ui->checkFileButton->setEnabled(true);
}
The program compiles, but it crashes right after start. I assume that I do something wrong with slots and signals, but can't find my mistake.
The reason for crash is that you are not making any instance of the class MockClamWrapper. In the connect statement, you are referencing a pointer that points to nothing. Make a new object and then connect :
clam = new MockClamWrapper();
connect(clam, SIGNAL(databaseLoaded()), this, SLOT(enableWindow()));

How can I emit signal form another class?

How can i emit signal from another class? In my implementation shown below I've got "unresolved external symbol error" when I try to emit signal in SerialPort::open method.
Code, head file of main window:
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
std::shared_ptr<SerialPort> serialPort;
private slots:
void labelchange();
private:
Ui::MainWindow *ui;
};
and the cpp file:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
serialPort(std::shared_ptr<SerialPort>(new SerialPort))
{
ui->setupUi(this);
connect(serialPort.get(), SIGNAL(opened()),this,SLOT(labelchange()));
}
void MainWindow::labelchange()
{
ui->testinLabel->setText("signal connected to slot");
}
and the other class method when i try emit signal:
head file:
class SerialPort : public QObject
{
public:
SerialPort();
void open()
signals:
void serial_opened();
}
and cpp file:
void SerialPort::open()
{
emit serial_opened();
}
This is just normal signal emitting from a class. Not "from another" class.
You are missing the Q_OBJECT macro, QObject do not really work with out it:
class SerialPort : public QObject
{
Q_OBJECT
public:
SerialPort();
void open()
signals:
void serial_opened();
}
And you need to have the file processed by moc. (happens automatically, if the files are listed in the .pro file)
SerialPort shall contain Q_OBJECT marco like this:
class SerialPort : public QObject
{
Q_OBJECT
public:
SerialPort();
void open()
signals:
void serial_opened();
}
Also please check your .pro file and check whether you added your SerialPort.h under HEADERS section.