Solved same signal-slot connection in two windows
-
@JonB Thanks a lot for your feedback.
Commenting out of
tetra_grip_api api
in themain.cpp
did end up in multiple errors with "Unresolved external symbol class tetra_grip_api api".From the suggestions from you guys, I feel like there is something wrong in making use of global variables. I should try also the other way of passing pointer to the class.
But I was trying to understand a reason program behaves this way.
As I mentioned earlier , If I call
Patients
behaves badly if I call it fromSettings
. But instead If I call bothPatieints
andSettings
in themain
(as shown below), both behave as expected. Do you immedatly think of any reason for this!?#include <QApplication> tetra_grip_api api; int main(int argc, char *argv[]) { QApplication a(argc, argv); api.openSerialPort(); QObject::connect(api.serial, SIGNAL(readyRead()), &api, SLOT(readData())); QObject::connect(api.serial, SIGNAL(error(QSerialPort::SerialPortError)),&api, SLOT(ErrorHandler(QSerialPort::SerialPortError))); // error handling Settings w(nullptr); //---->this behaves right Patients v(nullptr); //---- >this behaves right v.show(); w.show(); return a.exec(); }
-
@russjohn834
No, because you show the case which works OK. We need to see exactly howSettings
callsPatients
, plus the signal connections, with whatever is relevant to that case, to judge where the problem is. -
This is how I call
Patients
fromSettings
void Settings::on_pushButton_patients_clicked() { this->close(); stagetwo = new Patients(this); stagetwo -> show(); }
where
stagetwo
is publicpublic: Settings(QString,QWidget *parent = nullptr); ~Settings(); Patients *stagetwo;
In the
Patients
signal connection is as below:Patients::Patients( QWidget *parent) : QMainWindow(parent), ui(new Ui::Patients) { ui->setupUi(this); connect(&api, &tetra_grip_api::tetraGripEvent,this, &StageTwoPatients::eventHandlerTwo); }
-
@russjohn834 said in same signal-slot connection in two windows:
void Settings::on_pushButton_patients_clicked()
{
this->close();
stagetwo = new Patients(this);
stagetwo -> show();
}Just to humour me, replace by
void Settings::on_pushButton_patients_clicked() { // this->close(); // stagetwo = new Patients(this); stagetwo = new Patients(nullptr); stagetwo -> show(); }
Any better behaviour?
-
Can't see any change.
-
@russjohn834
Very last thing: how do you know slot is not being called? Please place a breakpoint on it. -
@JonB
I did try with a breakpoint, and convinced that the slot is not being called when thePatients
window is constructed.
So I guess that's a serious issue using global class instance. Though I was trying to understand why this is happening .. -
@russjohn834
There should be no problem. I don't know what the difference is in your working vs non-working situations, I'm afraid. -
@mrjj
can you think of a reason why this second scenario not working?I have signal connection in two windows ,
Settings
andPatients
In
Settings
:Settings::Settings( QWidget *parent) : QMainWindow(parent), ui(new Ui::Settings) { ui->setupUi(this); connect(&api, &tetra_grip_api::tetraGripEvent,this, &Settings::eventHandler); ... }
api
is global instance of a class.And in
Patients
:Patients::Patients( QWidget *parent) : QMainWindow(parent), ui(new Ui::Patients) { ui->setupUi(this); connect(&api, &tetra_grip_api::tetraGripEvent,this, &Patients::eventHandlerTwo); }
Situation1 - Working
I construct both
Settings
andPatients
window fromMain
#include <QApplication> tetra_grip_api api; int main(int argc, char *argv[]) { QApplication a(argc, argv); api.openSerialPort(); QObject::connect(api.serial, SIGNAL(readyRead()), &api, SLOT(readData())); Settings w(nullptr); //---->this behaves right Patients v(nullptr); //---- >this behaves right v.show(); w.show(); return a.exec(); }
By working I mean , both the slots are being called , and
QLabel
set text accordinglySituation 2 - Not working
I call
Patients
fromSettings
:void Settings::on_pushButton_patients_clicked() { this->close(); stagetwo = new Patients(this); stagetwo -> show(); }
where
stagetwo
is publicpublic: Settings(QString,QWidget *parent = nullptr); ~Settings(); Patients *stagetwo;
Here
Settings
works just fine (slot being called) but slot inPatients
,eventhandlerTwo
is not being called at all. -
@russjohn834 Can you test what happens if you
hide()
the Settings instead ofclose()
them?void Settings::on_pushButton_patients_clicked() { this->hide(); stagetwo = new Patients(this); stagetwo -> show(); }
-
@jazzco2 Did try that, in both case (
hide
orclose
) slot in thePatients
not being called -
@russjohn834
Try commenting out inSettings::Settings
connect(&api, &tetra_grip_api::tetraGripEvent,this, &Settings::eventHandler);
Let's not have
Settings
connected to the signal while we sort outPatients
. -
Ok, I just wondered what happens, because
close()
may delete yourSettings
afterwards and you have no access to neitherSettings
norPatients
. What about the other tests:-
Did it help to pass the pointer to Settings and to Patients in the constructor?
-
In what order are the debug messages when you add them in
Settings::eventHandler()
and after connecting and disconnecting (destructor) in Settings and Patients? (I wonder if the connection is cut off before it is called)
-
-
@jazzco2
I suggested earlier that OP run for now withvoid Settings::on_pushButton_patients_clicked() { // this->close(); // stagetwo = new Patients(this); stagetwo = new Patients(nullptr); stagetwo -> show(); }
Although it apparently did not solve, I suggest he revert to that code while sorting this out. It will remove some irrelevant paths of investigation.
-
Maybe it would be easier if the OP could provide a minimal, compilable example of his problem so we can try it out by ourself. I see a good chance here that the current code is that confusing that no suggestion from here will ever help.
-
@JonB Yes that's fine. It also should be ensured that
-
both
&api
references point to the same instance.
-> add debug messages at eachconnect(..)
to display the pointer -
the connection is alive
-> add several debug messages as I pointed out earlier -
the signal
tetraGripEvent()
is emitted in both cases
-> keepSettings
alive (usinghide()
instead ofclose()
) and add a debug message inSettings::eventHandler
-
-
@Christian-Ehrlicher
Please find here the minimal version of both the class files (.cpp and .h and main)main.cpp
#include "settings.h" #include "tetra_grip_api.h" #include <QApplication> tetra_grip_api api; int main(int argc, char *argv[]) { QApplication a(argc, argv); api.openSerialPort(); QObject::connect(api.serial, SIGNAL(readyRead()), &api, SLOT(readData())); Settings w(nullptr); w.show(); return a.exec(); }
Settings.cpp
#include "settings.h" #include "ui_settings.h" #include "tetra_grip_api.h" Settings :: Settings (QWidget *parent) : QMainWindow(parent) QMainWindow(parent) , ui(new Ui::Settings ) { ui->setupUi(this); connect(&api, &tetra_grip_api::tetraGripEvent,this, &Settings::eventHandler); } Settings ::~Settings () { delete ui; } void Settings::on_pushButton_exit_clicked() { QApplication::quit(); } void Settings::on_pushButton_patients_clicked() { this->hide(); stagetwo = new Patients(this); stagetwo -> show(); } void Settings::eventHandler(STIM_GUI_TOPIC_T topic, uint8_t reg, uint32_t value) { if (topic==TOPIC_STIMULATOR) { switch(reg) { case STIM_REG_BATTERY_CAPACITY_REMAINING: ui->label_statusBat->setText("Battery remaining: "+QString::number(value)+"%"); break; } } }
settings.h
#ifndef SETTINGS_H #define SETTINGS_H #include <QMainWindow> #include "patients.h" #include <QtSerialPort/QSerialPort> #include "tetra_grip_api.h" #include <QDebug> QT_BEGIN_NAMESPACE namespace Ui { class Settings; } QT_END_NAMESPACE class Settings : public QMainWindow { Q_OBJECT public: Settings(QWidget *parent = nullptr); ~Settings(); Patients *stagetwo; private slots: void on_pushButton_exit_clicked(); void on_pushButton_patients_clicked(); public slots: void eventHandler(STIM_GUI_TOPIC_T topic, uint8_t reg, uint32_t value); private: Ui::Settings *ui; }; #endif
patients.cpp
(I'm giving a bit more details here because here the slot not being called fromSettings
)..I have a table here , user can select an entry to do something else...#include "patients.h" #include "ui_patients.h" #include "settings.h" #include <QDebug> #include <QFile> #include <QDirIterator> #include <QXmlStreamReader> #include <QMessageBox> #include "tetra_grip_api.h" Patients::Patients(QWidget *parent) : QMainWindow(parent), ui(new Ui::Patients) { ui->setupUi(this); connect(&api, &tetra_grip_api::tetraGripEvent,this, &Patients::eventHandlerTwo); // Setup table ui->tableWidget->setColumnCount(5); ui->tableWidget->setHorizontalHeaderLabels(QStringList{"Patient ID","Name", "Surname", "LastSession", "Note"}); ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); QString path = QCoreApplication::applicationDirPath()+"/data/"; QStringList qdiFilter("*.xml"); // patientData data; QDirIterator qdi( path, qdiFilter, QDir::Files); while (qdi.hasNext()) { parseDataEntry(qdi.next()); //data.parseDataEntry(qdi.next()); } } void Patients::parseDataEntry(const QString dataPath) { QString patientID, firstName, surName, lastSession = "", pNote; // Load our XML file. QFile *xmlFile; xmlFile = new QFile(dataPath); if(!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::critical(this, "Error Loading Patient Data File", QString("Could not load the patient XML data file at:\r\n %0").arg(dataPath), QMessageBox::Ok); return; } // Create an XML reader. QXmlStreamReader *xmlReader; xmlReader = new QXmlStreamReader(xmlFile); // Parse each element of the XML until we reach the end. while(!xmlReader->atEnd() && !xmlReader->hasError()) { // Read next element QXmlStreamReader::TokenType token = xmlReader->readNext(); // If token is just StartDocument - go to next if(token == QXmlStreamReader::StartDocument) { continue; } // If token is StartElement - read it if(token == QXmlStreamReader::StartElement) { if(xmlReader->name() == "Name") { firstName = xmlReader->readElementText(); } else if(xmlReader->name() == "Surname") { surName = xmlReader->readElementText(); } else if(xmlReader->name() == "Patient_ID") { patientID = xmlReader->readElementText(); } else if(xmlReader->name() == "Date") { lastSession = xmlReader->readElementText(); } else if(xmlReader->name() == "Clinician_Note") { pNote = xmlReader->readElementText(); } } } if(xmlReader->hasError()) { QMessageBox::critical(this, "Error Parsing Patient Data File", QString("Error reading the patient file at:\r\n %0,\r\nError: %1").arg(dataPath, xmlReader->errorString()), QMessageBox::Ok); return; } // close reader and flush file xmlReader->clear(); xmlFile->close(); // Delete delete xmlFile; delete xmlReader; // Add a new row to the table, with the data we parsed. ui->tableWidget->insertRow(ui->tableWidget->rowCount()); ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 0, new QTableWidgetItem(patientID)); ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 1, new QTableWidgetItem(firstName)); ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 2, new QTableWidgetItem(surName)); ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 3, new QTableWidgetItem(lastSession)); ui->tableWidget->setItem(ui->tableWidget->rowCount() - 1, 4, new QTableWidgetItem(pNote)); // ui->tableWidget->setCellWidget(ui->tableWidget->rowCount()-1,3, new QTableWidgetItem(lastSession)) } Patients::~Patients() { delete ui; } void Patients::on_pushButton_Open_clicked() { QModelIndexList selection=ui->tableWidget->selectionModel()->selectedRows(0); qDebug()<<"\n The content is"<<selection[0].data().toString(); ... } void Patients::eventHandlerTwo( STIM_GUI_TOPIC_T topic, uint8_t reg, uint32_t value) { ui->label_test->setText("Slot called"); }
and
patients.h
#ifndef PATIENTS_H #define PATIENTS_H #include <QMainWindow> #include "tetra_grip_api.h" namespace Ui { class Patients; } class Patients : public QMainWindow { Q_OBJECT public: Patients( QWidget *parent = nullptr); ~Patients(); void parseDataEntry(const QString dataPath); public slots: void eventHandlerTwo(STIM_GUI_TOPIC_T topic, uint8_t reg, uint32_t value); void on_pushButton_Open_clicked(); private: Ui::Patients *ui; }; #endif
and the API class
tetra_grip_api.h
#ifndef TETRA_GRIP_API_H #define TETRA_GRIP_API_H #include <QObject> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <string> #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo> #include <QLabel> #include <QMessageBox> #define _CRT_SECURE_NO_DEPRECATE class tetra_grip_api : public QObject { Q_OBJECT public: explicit tetra_grip_api(QObject *parent = nullptr); #define MAX_CONFIG_FILE_LENGTH (10000) bool static send_short_block(STIM_GUI_MESSAGE_S_BLOCK_T *pblock); void static battery_percentage(void); QSerialPort *serial = nullptr; signals: void tetraGripEvent(STIM_GUI_TOPIC_T topic, uint8_t reg, uint32_t value); public slots: void openSerialPort(); void closeSerialPort(); void readData(); private: QString comPortName; }; extern tetra_grip_api api; #endif // TETRA_GRIP_API_H
and
tetra_grip_api.cpp
#include "tetra_grip_api.h" #include "tetra_grip_reporter.h" #include <QDebug> #include <stdio.h> #include <stdlib.h> #define _CRT_SECURE_NO_DEPRECATE using namespace::std; tetra_grip_api::tetra_grip_api(QObject *parent) : QObject(parent) { } void tetra_grip_api::openSerialPort() { serial = new QSerialPort(); QList <QSerialPortInfo>stim; QSerialPortInfo info; foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { if(info.description() == "USB Serial Port" && info.manufacturer() == "FTDI" && QString::number(info.vendorIdentifier(), 16)== "403" && QString::number(info.productIdentifier(), 16)== "6015") { comPortName = info.portName(); } } serial->setPortName(comPortName); serial->setBaudRate(1000000); / serial->setDataBits(QSerialPort::Data8); serial->setParity(QSerialPort::NoParity); serial->setStopBits(QSerialPort::OneStop); serial->setFlowControl(QSerialPort::HardwareControl); if(serial->open(QIODevice::ReadWrite)) { qDebug()<<"Opend port OK"; } else { qDebug()<<"Failed to open port. Error code: "<< serial->error() << serial->errorString(); } } void tetra_grip_api::readData() { const QByteArray data = api.serial->readAll(); STIM_GUI_PROTOCOL_Process_Received_Bytes((uint8_t*)data.data(), (size_t)data.length()); } void tetra_grip_api::closeSerialPort() { if (serial->isOpen()) serial->close(); } void tetra_grip_reporter(STIM_GUI_TOPIC_T topic, uint8_t reg, uint32_t value) { emit api.tetraGripEvent(topic, reg, value); }
-
@russjohn834 said in same signal-slot connection in two windows:
StageTwoPatients::~StageTwoPatients() { delete ui; }
What is this definition doing in the middle of the
Patients
class?Meanwhile, I don't see any
~Patients()
definition as per declared in the header file. -
@russjohn834 said in same signal-slot connection in two windows:
tetra_grip_api api;
I would rename this to something other, use an own 'extern' definition and try again.
-
Sorry about that typo. Originally the name of my class was a bit lengthier so I use
Settings
andPatients
for better understanding..