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)) );
Related
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.
I have created a class named MainWIndow which has a method(getconnOpen) to provide database connection! following is my code!
MainWindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
QSqlDatabase mydb;
bool getconnOpen(QString uname,QString pword,QString ip,int port,QString dbname){
mydb=QSqlDatabase::addDatabase("QOCI");
mydb.setUserName(uname);
mydb.setPassword(pword);
mydb.setHostName(ip);
mydb.setPort(port);
mydb.setDatabaseName(dbname);
mydb.open
return true;
}
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
MainWindow.cpp
void MainWindow::on_pushButton_clicked()
{
Dialog *dialog1=new Dialog(this);
if(getconnOpen(ui->lineEdit->text(),ui->lineEdit_2->text(),ui->lineEdit_3->text(),ui->lineEdit_4->text().toInt(),ui->lineEdit_5->text()){
dialog1->show();
}
}
Dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:QSqlDatabase mydb;
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
Dialog.cpp
void Dialog::on_pushButton_clicked()
{
QSqlQueryModel modal = new QSqlQueryModel();
QSqlQuery qry=new QSqlQuery(mydb);
qry->prepare("select User FROM USERS");
qry->exec();
modal->setQuery(*qry);
ui->tableView->setModel(modal);
}
Though I opened the connection in MainWindow.h the query in dialog.cpp gives an error stating "database not open" ! But I opened the databse in MainWindow.cpp! How can I correct this?
Try something like this (mind you I do not have time to test this so you may need to tweak it) And I am annoyed that it created a cyclical dependence between the MainWindow and Dialog.. you can undo that by creating a dbmanager class or using a shared pointer to the mydb instead. but this is the quickest thing I could type for you without having to redesign all your code.
MainWindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
QSqlDatabase mydb;
bool getconnOpen(QString uname,QString pword,QString ip,int port,QString dbname){
mydb=QSqlDatabase::addDatabase("QOCI");
mydb.setUserName(uname);
mydb.setPassword(pword);
mydb.setHostName(ip);
mydb.setPort(port);
mydb.setDatabaseName(dbname);
// Return the actual state of the db instead of return true.
return mydb.open();
}
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
MainWindow.cpp
void MainWindow::on_pushButton_clicked()
{
Dialog *dialog1=new Dialog(this);
if(getconnOpen(ui->lineEdit->text(),ui->lineEdit_2->text(),ui->lineEdit_3->text(),ui->lineEdit_4->text().toInt(),ui->lineEdit_5->text()){
dialog1->show();
}
}
Dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
Dialog.cpp
void Dialog::on_pushButton_clicked()
{
QSqlQueryModel modal = new QSqlQueryModel();
// get the db object from the parent widget, if the parent widget returned is not of the MainWindow type then dynamic_cast will return NULL.
MainWindow *mainWindow = dynamic_cast<MainWindow(parentWidget());
// if all went well then mainWindow is not NULL but should test first.
if (mydb != NULL)
{
QSqlQuery qry=new QSqlQuery(mainWindow->mydb);
qry->prepare("select User FROM USERS");
qry->exec();
modal->setQuery(*qry);
ui->tableView->setModel(modal);
}
// do something else if the pointer is NULL.
}
I am not happy with doing it this way:
*********** OK FOUND THE RIGHT WAY TO DO IT I THINK!*********************
from this post Handling QSqlDatabase connections
looks like QSqlDatabase is already a connection manager so you add the connection once and retrieve it by name from your other form. Again I do not have the ability to test this but this would be a better approach as it uses the QT connection manager as it is intended and there are no cyclical dependencies between MainWindow and the Dialog.
MainWindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
QSqlDatabase mydb;
bool getconnOpen(QString uname,QString pword,QString ip,int port,QString dbname){
// Add the database by name so you can retrieve it later. I would also
// not hardcode the name like this but this.. yuck but hey you can fix
// that yourself ;)
mydb=QSqlDatabase::addDatabase("QOCI", "MyDbConnection");
mydb.setUserName(uname);
mydb.setPassword(pword);
mydb.setHostName(ip);
mydb.setPort(port);
mydb.setDatabaseName(dbname);
// Return the actual state of the db instead of return true.
return mydb.open();
}
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
MainWindow.cpp
void MainWindow::on_pushButton_clicked()
{
Dialog *dialog1=new Dialog(this);
if(getconnOpen(ui->lineEdit->text(),ui->lineEdit_2->text(),ui->lineEdit_3->text(),ui->lineEdit_4->text().toInt(),ui->lineEdit_5->text()){
dialog1->show();
}
}
Dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
Dialog.cpp
void Dialog::on_pushButton_clicked()
{
QSqlQueryModel modal = new QSqlQueryModel();
// make sure the database exists.
if (QSqlDatabase::contains("MyDbConnection");)
{
// retrieve the database by name from the database manager.
QSqlQuery qry=new QSqlQuery(QSqlDatabase::database("MyDbConnection"));
qry->prepare("select User FROM USERS");
qry->exec();
modal->setQuery(*qry);
ui->tableView->setModel(modal);
}
// do something else if the pointer is NULL.
}
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");
}
};
}
in my Qt program I need the dialog to send a signal to a slot in the mainwindow. I have set the connection up correctly to the best of my knowledge and it does not give me any errors during compile or run time but for some reason it just doesn't do anything when the button that is supposed to activate the signal is clicked. Why is this happening?
beastiary.h (mainwindow header)
namespace Ui {
class Beastiary;
}
class Beastiary : public QMainWindow
{
Q_OBJECT
public:
explicit Beastiary(QWidget *parent = 0);
Ui::Beastiary *ui;
QStringList MyList;
~Beastiary();
public slots:
void refresh();
private slots:
void on_pushButton_clicked();
void on_listWidget_itemClicked(QListWidgetItem);
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
beastiary.cpp (mainwindow cpp file)
Beastiary::Beastiary(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Beastiary)
{
ui->setupUi(this);
Dialog dialog;
connect(&dialog, SIGNAL(gorefresh()),this, SLOT(refresh())) ;
void Beastiary::refresh()
{
qDebug () << "recieved";
ui->listWidget->clear();
QString path = "C:/Program Files/Bargest/bin";
QDir myPath(path);
myPath.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
MyList = myPath.entryList();
ui->listWidget->addItems(MyList);
}
dialog.h
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
signals:
void gorefresh();
private slots:
void on_pushButton_done_clicked();
void on_pushButton_cancel_clicked();
void on_pushButton_clicked();
private:
Ui::Dialog *ui;
dialog.cpp
void Dialog::on_pushButton_done_clicked()
{
emit gorefresh();
}
I did leave out large parts of the code that just have nothing to do with the actual signals and slots mechanism at play here.
You're only connecting the dialog instance you created locally in the Bestiary's constructor, which goes out of scope as the constructor finishes.
connect is connecting instances, not classes. That means you need to connect each created dialog:
void Beastiary::on_pushButton_clicked() {
Dialog* ad = new Dialog(this);
connect(ad, SIGNAL(gorefresh()), this, SLOT(refresh()));
ad->show();
}
While at it, you should seriously consider using the type-safe connect syntax that came with Qt 5:
void Beastiary::on_pushButton_clicked() {
Dialog* ad = new Dialog(this);
connect(ad, &Dialog::gorefresh, this, &Bestiary::refresh));
ad->show();
}
I made a simple Qt project to cover the issue of calling Ui from another class.
The Files:
mainwindow.h | mainwindow.cpp | client.h | client.cpp | main.cpp
The Issue:
Connecting a signal from client.cpp to a slot in mainwindow.cpp worked very well.
But when I added a ui->statusBar->showMessage("message");
in the slot, it didn't work.
NOTE: When I made the signal and the slot both in mainwindow.cpp it worked, but calling a slot from mainwindow.cpp from a signal and connect() in client.cpp doesn't work.
The Codes: (trimmed to the essentials)
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
public slots:
void doSomething();
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::doSomething()
{
QMessageBox::information(0, "test", "BINGO!!");
ui->statusBar->showMessage("testing");
}
client.h
class client : public QObject
{
Q_OBJECT
public:
explicit client(QObject *parent = 0);
void call();
signals:
void connected();
public slots:
};
client.cpp
client::client(QObject *parent) :
QObject(parent)
{
MainWindow main;
connect(this, SIGNAL(connected()), &main, SLOT(doSomething()));
call();
}
void client::call()
{
emit connected();
}
Added:
main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Thanks.
client::client(QObject *parent) :
QObject(parent)
{
MainWindow main;
connect(this, SIGNAL(connected()), &main, SLOT(doSomething()));
call();
}
Your MainWindow lives on the stack. I think by the time your doSomething slot is triggered, the MainWindow object is already long gone. Try creating your MainWindow on the heap instead.
I see a major mistake in the code you have posted.
You have created an instance of MainWindow in the file main.cpp and thats the window you see when you run the application. But in your client.cpp you are creating a second instance of MainWindow and connecting the signal from the client object to the slot of the second MainWindow object you create. You are not seeing two main windows because you do not run the exec or show methods of this second instance.
Now coming to the solution, you will have to connect the signal from client to the proper MainWindow instance. I will suggest two methods below.
First:
I believe you create an instance of client in one of the member functions of the class MainWindow. If so then shift the connect method to the file mainwindow.cpp
Something like:
void MainWindow::someFunction()
{
client* c = new client();
connect(c, SIGNAL(connected()), this, SLOT(doSomething()));
c->call();
}
Second:
If you cannot do what is explained above, then you can use a combination of a static pointer and a static function to get the instance of MainWindow. Note that this method can also be used to ensure that there is only one instance of MainWindow at any given time.
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
public slots:
void doSomething();
public:
explicit MainWindow(QWidget* parent = 0);
~MainWindow();
static MainWindow* GetInstance(QWidget* parent = 0);
private:
Ui::MainWindow *ui;
static MainWindow* mainInstance;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow* MainWindow::mainInstance = 0;
MainWindow* MainWindow::GetInstance(QWidget *parent)
{
if(mainInstance == NULL)
{
mainInstance = new MainWindow(parent);
}
return mainInstance;
}
void MainWindow::doSomething()
{
QMessageBox::information(0, "test", "BINGO!!");
ui->statusBar->showMessage("testing");
}
Now in whatever class you need to get the instance of MainWindow in use, simply use the static function MainWindow::GetInstance to get the pointer to the MainWindow instance and use that as the parameter for connect.
i.e.,
MainWindow* instance = MainWindow::GetInstance();
client* c = new client();
connect(c, SIGNAL(connected()), instance , SLOT(doSomething()));