QT Widget class inheriting other classes to use other methods
-
Hello. I am new to c++. I have watched a couple of videos and tutorials but I would like to get good advice on what is the correct and best way to do certain things when it comes to class inheritance.
For example. I have created a serial component:
header file:
#ifndef SERIAL_H #define SERIAL_H #include <QSerialPort> #include <QSerialPortInfo> class Serial { public: QSerialPort* serial; Serial(); void Connect_to_device(); void Serial_received(); void Serial_write(QByteArray data); void Scan_serial_devices(); }; #endif // SERIAL_H
cpp file:
#include "serial.h" Serial::Serial() { serial = new QSerialPort; } void Serial::Connect_to_device(){ serial->setPortName("COM3"); serial->setBaudRate(QSerialPort::Baud115200); serial->setDataBits(QSerialPort::Data8); serial->setParity(QSerialPort::NoParity); serial->setStopBits(QSerialPort::OneStop); serial->setFlowControl(QSerialPort::NoFlowControl); if (serial->open(QIODevice::ReadWrite)) { qDebug("connected sucesfully \n"); } else { qDebug("connected failed \n"); } } void Serial::Serial_received(){ qDebug("serial received = %s",serial->readAll().toStdString().c_str()); } void Serial::Serial_write(QByteArray data){ qDebug("Trying to write data =%s \n",data.toStdString().c_str()); serial->write(data); } void Serial::Scan_serial_devices(){ QString description; QString manufacturer; QString serialNumber; const auto infos = QSerialPortInfo::availablePorts(); for (const QSerialPortInfo &info : infos) { description = info.description(); manufacturer = info.manufacturer(); serialNumber = info.serialNumber(); qDebug("description%s \n",description.toStdString().c_str()); qDebug("manufacturer%s \n",manufacturer.toStdString().c_str()); qDebug("serialNumber%s \n",serialNumber.toStdString().c_str()); } }
As you can see, I have implemented function Scan_serial_devices. This function scans all available serial devices which I will later want to display.
I would like to call this function when button widget is clicked. In widget.cpp I have an event on pushbutton_2_clicked and I would like to call that function there, but since widget class does not know anything about serial class, It wont let me do it.
void Widget::on_pushButton_2_clicked() { s.Scan_serial_devices(); // does not work because s is not known here }
s is serial class object and is declared in main.cpp:
#include "widget.h" #include <QApplication> #include "serial.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; Serial s; w.show(); return a.exec(); }
Can someone give me an idea what is the correct way to use other class functions within widget class?
-
@lukutis222 said in QT Widget class inheriting other classes to use other methods:
does not work because s is not known here
Simply define s as class member in your Widget class:
#include "serial.h" class Widget... { private: Serial s;
Or, if you need it also in other places, pass pointer to it to Widget instance:
int main(int argc, char *argv[]) { QApplication a(argc, argv); Serial s; Widget w(&s); w.show(); return a.exec(); }
Of course you will need to change Widget constructor, so it can take a pointer to Serial and store this pointer in a class member in Widget.
Better way in a Qt application would be to use signals and slots: define signals in Serial to distribute information and add slots in Widget where you use this information. Then connect these signals and slots. See https://doc.qt.io/qt-6/signalsandslots.html
-
Hello. Thank you very much for your response. Sorry for taking some time to coming back to this. I would like to clarify the following:
If I create an object in my main.cpp
int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; Serial s; //Serial_list->addItems("test"); w.show(); return a.exec(); }
And then I define s as class member in my other widget class:
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "serial.h" QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private slots: void on_Serial_list_activated(int index); void on_Serial_connect_clicked(); void on_Scan_button_clicked(); void on_Baudrate_select_activated(int index); private: Ui::Widget *ui; Serial s; }; #endif // WIDGET_H
Wouldnt that create a completely different instance of my Serial class object? How do I know for sure that it is referring to the same object instance since I can have multiple Serial class objects
-
@lukutis222 said in QT Widget class inheriting other classes to use other methods:
Wouldnt that create a completely different instance of my Serial class object?
Of course, but if only Widget needs Serial, then simply remove s from main...
If you need Serial also in other places then take a look at my other suggestions. -
@lukutis222
In the code you show there is no relationship between theSerial s;
inmain()
versus the one in theprivate
section ofWidget
. Other than they are both of classSerial
, they are quite distinct instances, and know nothing about each other. Does that answer your question?Earlier @jsulm was advising you to use one or the other of these, not both.
-
Yes now it is clear. Thank you very much for confirming
-
Sorry for getting back to it after a long time, but I would like to understand how to correctly pass a class object pointer to another class constructor as you shown in the example:
int main(int argc, char *argv[]) { QApplication a(argc, argv); Serial s; Widget w(&s); w.show(); return a.exec(); }
So I have a Serial class and my serial.h looks like:
#ifndef SERIAL_H #define SERIAL_H #include <QWidget> #include "QSerialPort" #include "QSerialPortInfo" #include "stdio.h" #include "stdint.h" class Serial : public QWidget { Q_OBJECT public: explicit Serial(QWidget *parent = nullptr); QSerialPort serial_connection; // this keeps all information regarding the serial connection QString available_devices[10]; uint8_t device_counter; void Scan_serial_devices(); bool Serial_connect(); bool Serial_disconnect(); void write_data(QByteArray data); bool is_data_available(); QByteArray read_data(); void Set_Baudrate(int32_t baudrate); void Set_Portname(QString name); void Set_Databits(QSerialPort::DataBits dataBits); void Set_Stopbits(QSerialPort::StopBits stopBits); void Set_Flowcontrol(QSerialPort::FlowControl flowControl); void Set_Paritybits(QSerialPort::Parity parity); signals: private: QWidget *Widget; }; #endif // SERIAL_H
I create a class instance in my main.cpp and try to pass it to Widget class constructor. =
int main(int argc, char *argv[]) { QApplication a(argc, argv); Serial s; Widget w(&s); w.show(); return a.exec(); }
I have modified my widget class constructor as following:
Widget.cpp
Widget::Widget(QWidget *parent,Serial* serial_instance) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); fillPortsParameters(); //connect(&serial_obj->serial_connection, &QSerialPort::readyRead, this, &Widget::readData); }
Widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "serial.h" #include "logging.h" #include <QTime> #include <QDate> #include <QDir> #include <QListView> #include <QScrollArea> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr,Serial* serial_instance); ~Widget(); void fillPortsParameters(); private slots: void on_Scan_button_clicked(); void on_Serial_connect_button_clicked(); void readData(); void on_write_box_returnPressed(); void on_Scenario_select_currentIndexChanged(int index); private: Ui::Widget *ui; protected: }; #endif // WIDGET_H
I am not fully understanding why is it complaining:
Am I not passing a class object to class constructor correctly?
-
@lukutis222 said in QT Widget class inheriting other classes to use other methods:
how to correctly pass a class object pointer to another class constructor
That is what you are doing in your
Widget w(&s)
statement, passing a* Serial
(class object pointer) to theWidget
constructor (another class constructor).Now look at your
Widget(QWidget *parent = nullptr, Serial *serial_instance)
. This has a default for the first parameter and not for the second parameter, which is not allowed in C++. Optional parameters (those with a default) must come after mandatory ones (those without a default).This is a separate issue from whether your
Serial
class should be aQWidget
(why are you making it such, is it really any kind of widget?), and why it has aprivate: QWidget *Widget;
, what is that supposed to be for? -
You are totally right about My Serial class and it should not be QWidget. The reason why that happen is because I was generating this class automatically and accidentally select QWidget as base class. I have fixed my serial class :
serial.cpp
#include "serial.h" Serial::Serial() { } void Serial::Scan_serial_devices(){ QString description; QString manufacturer; QString serialNumber; QString portName; this->available_devices->clear(); this->device_counter = 0; const auto infos = QSerialPortInfo::availablePorts(); for (const QSerialPortInfo &info : infos) { description = info.description(); manufacturer = info.manufacturer(); serialNumber = info.serialNumber(); portName = info.portName(); //qDebug("description = %s \n",description.toStdString().c_str()); //qDebug("manufacturer = %s \n",manufacturer.toStdString().c_str()); //qDebug("serialNumber = %s \n",serialNumber.toStdString().c_str()); //qDebug("Portname = %s \n",portName.toStdString().c_str()); available_devices[device_counter] = portName; device_counter++; } } bool Serial::Serial_connect(){ if(serial_connection.open(QIODevice::ReadWrite)){ qDebug("Connection is succesfull \n"); return 1; } else { //error qDebug() << serial_connection.errorString(); } return 0; } bool Serial::Serial_disconnect(){ serial_connection.close(); return 1; } void Serial::Set_Baudrate(int32_t baudrate){ serial_connection.setBaudRate(baudrate); } void Serial::Set_Portname(QString name){ serial_connection.setPortName(name); } void Serial::Set_Databits(QSerialPort::DataBits dataBits){ serial_connection.setDataBits(dataBits); } void Serial::Set_Stopbits(QSerialPort::StopBits stopBits){ serial_connection.setStopBits(stopBits); } void Serial::Set_Paritybits(QSerialPort::Parity parity){ serial_connection.setParity(parity); } void Serial::Set_Flowcontrol(QSerialPort::FlowControl flowControl){ serial_connection.setFlowControl(flowControl); } void Serial::write_data(QByteArray data){ serial_connection.write(data); } bool Serial::is_data_available(){ return (serial_connection.canReadLine()); } QByteArray Serial::read_data(){ QByteArray line = serial_connection.readLine(); return line; }
serial.h
#ifndef SERIAL_H #define SERIAL_H #include "QSerialPort" #include "QSerialPortInfo" #include "stdio.h" #include "stdint.h" #include <qDebug> class Serial { public: Serial(); QSerialPort serial_connection; // this keeps all information regarding the serial connection QString available_devices[10]; uint8_t device_counter; void Scan_serial_devices(); bool Serial_connect(); bool Serial_disconnect(); void write_data(QByteArray data); bool is_data_available(); QByteArray read_data(); void Set_Baudrate(int32_t baudrate); void Set_Portname(QString name); void Set_Databits(QSerialPort::DataBits dataBits); void Set_Stopbits(QSerialPort::StopBits stopBits); void Set_Flowcontrol(QSerialPort::FlowControl flowControl); void Set_Paritybits(QSerialPort::Parity parity); signals: private: }; #endif // SERIAL_H
Now going back to the main subject, when I try to pass a pointer to a Serial class object, I get the following error:
C:\Users\petrikas.lu\Desktop\WORK\QT\UTB\UTB_gui\main.cpp:13: error: no matching function for call to 'Widget::Widget(Serial*)' ..\UTB_gui\main.cpp: In function 'int qMain(int, char**)': ..\UTB_gui\main.cpp:13:16: error: no matching function for call to 'Widget::Widget(Serial*)' 13 | Widget w(&s); | ^
In my widget.h I have modified the class constructor but I must be missing something else.
QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr,Serial* serial_ptr = nullptr); ~Widget();
And in my widget.cpp I also added a parameter to a widget class constructor:
Widget::Widget(QWidget *parent, Serial* serial_ptr) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); fillPortsParameters(); //connect(&serial_obj->serial_connection, &QSerialPort::readyRead, this, &Widget::readData); } Widget::~Widget() { delete ui; }
This might be confusing for me because for the widget class constructor it supposed to take 2 parameters:
Widget::Widget(QWidget *parent, Serial* serial_ptr)
but I only pass 1 parameter, that is not correct right?
-
@lukutis222 said in QT Widget class inheriting other classes to use other methods:
no matching function for call to 'Widget::Widget(Serial*)'
Well that's right, isn't it, because your constructor is
Widget(QWidget *parent = nullptr,Serial* serial_ptr = nullptr);
, so how could that match your attempt to passWidget w(&s);
which has aSerial *
as its first parameter?Presumably you want its constructor to be
Widget(Serial* serial_ptr, QWidget *parent = nullptr);
.... It requires (i'm guessing that will be your intention for what you are writing) someSerial*
for its first argument, and accepts an optionalQWidget *
as a parent for its second parameter (else usesnullptr
for that value). -
@JonB said in QT Widget class inheriting other classes to use other methods:
Presumably you want its constructor to be Widget(Serial* serial_ptr, QWidget *parent = nullptr);....
Or simply pass nullptr for the first parameter...
-
Thank you both very much you are very kind @jsulm @JonB .
The last thing I did:
On my widget.h, I have declared a Serial variable that will be local for the Widget class but it will point to the global Serial class object.
QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Serial* serial_local; // this will be "local" instance of the Serial object that will point to the global Serial object. Widget(Serial* serial_ptr = nullptr, QWidget *parent = nullptr); ~Widget();
And in my widget class constructor, I assign it to the variable that I pass in my main.cpp:
Widget::Widget(Serial* serial_ptr,QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { serial_local = serial_ptr; ui->setupUi(this); fillPortsParameters(); serial_local->Test_print_message(); //connect(&serial_local->serial_connection, &QSerialPort::readyRead, this, &Widget::readData); }
Now my widget class can use all Serial methods via serial_local object. I believe that is correct now.
-
@lukutis222
You are getting there, but I would suggest you make an alteration.As I wrote earlier
It requires (i'm guessing that will be your intention for what you are writing) some
Serial*
for its first argumentNote the word "requires". That is why I did not suggest/agree with @jsulm's
Or simply pass nullptr for the first parameter...
That "works" (i.e. compiles), but may not be the right solution. Sure enough, your code goes
serial_local = serial_ptr; serial_local->Test_print_message();
But you have allowed the
serial_ptr
parameter to default tonullptr
. What will happen in your code if that is the case? Your whole intention is that this class must be given aSerial *
to use, right (else it's a pointless class)?Consequently, do not make it optional/default to
nullptr
. I said earlier the best signature is:Widget(Serial* serial_ptr, QWidget *parent = nullptr)
[or even
Serial &serial
, depending]Only make parameters optional/have a default if they really can be absent or have a sensible default, else make them mandatory.