Inheriting QSerialPort

2019-09-15 11:12发布

Perhaps a rather silly and newbie question but I have been struggling with keeping my QSerialPort serial; being used within the entirety of the application I am making. (Ughh this is frustrating to explain)

To be more clear I have the aforementioned QSerialPort serial; established in my MainWindow.cpp, but as I transition to another form which has a different class (for exampleoperations.cpp) I am unsure on how to keep and use my serial.* functions. My mainwindow.cpp form is just a connection settings form which allow you choose the port, baud rate, data bits, parity, etc to be set and once I press my "Open Connection" button, I have the form hidden(this->hide();) and the operations.cpp form appear.

Any clues on what should I do?

---

I had attempted to use Parent-Child relationship with the classes however, it only started a new QSerialPort serial; and the connect was lost.

1条回答
乱世女痞
2楼-- · 2019-09-15 11:41

You should factor out a separate QObject class that performs communications, and connect other classes to it.

A well designed system will never have a UI class own and use a serial port directly. See e.g. this answer for an idea how to separate the communications and the UI.

Let's see what transformations you could do to your code. At present you might have something similar to the below:

// https://github.com/KubaO/stackoverflown/tree/master/questions/serial-owner-41715726
#include <QtWidgets>
#include <QtSerialPort>

class Operations1 : public QWidget {
    Q_OBJECT
    QVBoxLayout m_layout{this};
    QPushButton m_send{"Send"};
    QPointer<QSerialPort> m_serial;
public:
    Operations1() {
        m_layout.addWidget(&m_send);
        connect(&m_send, &QPushButton::clicked, this, &Operations1::sendRequest);
    }
    void sendRequest() {
        QByteArray request;
        QDataStream ds(&request, QIODevice::WriteOnly);
        ds << qint32(44);
        m_serial->write(request);
    }
    void setSerial(QSerialPort * port) {
        m_serial = port;
    }
};

class MainWindow1 : public QWidget {
    Q_OBJECT
    QVBoxLayout m_layout{this};
    QPushButton m_open{"Open"};
    QSerialPort m_serial;
    QScopedPointer<Operations1> m_operations;
    Operations1 * operations() {
        if (!m_operations)
            m_operations.reset(new Operations1);
        return m_operations.data();
    }
public:
    MainWindow1() {
        m_layout.addWidget(&m_open);
        connect(&m_open, &QPushButton::clicked, this, &MainWindow1::open);
    }
    void open() {
        m_serial.setBaudRate(38400);
        m_serial.setPortName("/dev/tty.usbserial-PX9A3C3B");
        if (!m_serial.open(QIODevice::ReadWrite))
            return;
        operations()->show();
        operations()->setSerial(&m_serial);
    }
};

int main1(int argc, char ** argv) {
    QApplication app{argc, argv};
    MainWindow1 ui;
    ui.show();
    return app.exec();
}

The serial-port using functionality is spread across the UI classes, coupling them very tightly with the port. Let's fix that by factoring out the port operations:

class Controller2 : public QObject {
    Q_OBJECT
    QSerialPort m_port;
public:
    Controller2(QObject * parent = nullptr) : QObject{parent} {
        connect(&m_port, &QIODevice::bytesWritten, this, [this]{
            if (m_port.bytesToWrite() == 0)
                emit allDataSent();
        });
    }
    Q_SLOT void open() {
        m_port.setBaudRate(38400);
        m_port.setPortName("/dev/tty.usbserial-PX9A3C3B");
        if (!m_port.open(QIODevice::ReadWrite))
            return;
        emit opened();
    }
    Q_SIGNAL void opened();
    Q_SLOT void sendRequest() {
        QByteArray request;
        QDataStream ds(&request, QIODevice::WriteOnly);
        ds << qint32(44);
        m_port.write(request);
    }
    Q_SIGNAL void allDataSent();
};

class Operations2 : public QWidget {
    Q_OBJECT
    QVBoxLayout m_layout{this};
    QPushButton m_send{"Send"};
    QPointer<Controller2> m_ctl;
public:
    Operations2(Controller2 * ctl, QWidget * parent = nullptr) :
        QWidget{parent},
        m_ctl{ctl}
    {
        m_layout.addWidget(&m_send);
        connect(&m_send, &QPushButton::clicked, m_ctl, &Controller2::sendRequest);
    }
};

class MainWindow2 : public QWidget {
    Q_OBJECT
    QVBoxLayout m_layout{this};
    QPushButton m_open{"Open"};
    QPointer<Controller2> m_ctl;
    QScopedPointer<Operations2> m_operations;
    Operations2 * operations() {
        if (!m_operations)
            m_operations.reset(new Operations2{m_ctl});
        return m_operations.data();
    }
public:
    MainWindow2(Controller2 * ctl, QWidget * parent = nullptr) :
        QWidget{parent},
        m_ctl{ctl}
    {
        m_layout.addWidget(&m_open);
        connect(&m_open, &QPushButton::clicked, m_ctl, &Controller2::open);
        connect(m_ctl, &Controller2::opened, this, [this]{
            operations()->show();
        });
    }
};

int main2(int argc, char ** argv) {
    QApplication app{argc, argv};
    Controller2 controller;
    MainWindow2 ui(&controller);
    ui.show();
    return app.exec();
}

Finally, if you're tired of passing the controller around explicitly, we can implement a method akin to QCoreApplication::instance to get access to the unique controller instance:

class Controller3 : public QObject {
    Q_OBJECT
    QSerialPort m_port;
    static Controller3 * instance(bool assign, Controller3 * newInstance = nullptr) {
        static Controller3 * instance;
        if (assign)
            instance = newInstance;
        return instance;
    }
public:
    Controller3(QObject * parent = nullptr) : QObject{parent} {
        connect(&m_port, &QIODevice::bytesWritten, this, [this]{
            if (m_port.bytesToWrite() == 0)
                emit allDataSent();
        });
        instance(true, this);
    }
    ~Controller3() {
        instance(true);
    }
    Q_SLOT void open() {
        m_port.setBaudRate(38400);
        m_port.setPortName("/dev/tty.usbserial-PX9A3C3B");
        if (!m_port.open(QIODevice::ReadWrite))
            return;
        emit opened();
    }
    Q_SIGNAL void opened();
    Q_SLOT void sendRequest() {
        QByteArray request;
        QDataStream ds(&request, QIODevice::WriteOnly);
        ds << qint32(44);
        m_port.write(request);
    }
    Q_SIGNAL void allDataSent();
    static Controller3 * instance() {
        return instance(false);
    }
};

class Operations3 : public QWidget {
    Q_OBJECT
    QVBoxLayout m_layout{this};
    QPushButton m_send{"Send"};
public:
    Operations3(QWidget * parent = nullptr) : QWidget{parent}
    {
        m_layout.addWidget(&m_send);
        connect(&m_send, &QPushButton::clicked, Controller3::instance(), &Controller3::sendRequest);
    }
};

class MainWindow3 : public QWidget {
    Q_OBJECT
    QVBoxLayout m_layout{this};
    QPushButton m_open{"Open"};
    QScopedPointer<Operations3> m_operations;
    Operations3 * operations() {
        if (!m_operations)
            m_operations.reset(new Operations3);
        return m_operations.data();
    }
public:
    MainWindow3(QWidget * parent = nullptr) : QWidget{parent}
    {
        m_layout.addWidget(&m_open);
        connect(&m_open, &QPushButton::clicked, Controller3::instance(), &Controller3::open);
        connect(Controller3::instance(), &Controller3::opened, this, [this]{
            operations()->show();
        });
    }
};

int main3(int argc, char ** argv) {
    QApplication app{argc, argv};
    Controller3 controller;
    MainWindow3 ui;
    ui.show();
    return app.exec();
}
查看更多
登录 后发表回答