I am writing a gui in QT for controlling usb relays. I wrote a relay class which has turn on/off member functions. also wrote a custom widget which has radio buttons for controlling, a relay pointer, as well as a switchStatus() slot in which the relay turn on/off functions were called. The mainwindow has relay members. When I call the the ui->widget->switchStatus() from the mainwindow constructor, everything works fine, the relay can be turn on and off well. However, if I connect the radio button signal to switchStatus(), the program crashes whenever I click the radio button. It crashes at the line serialPort->write. But it's not about write, whatever code related to serialPort pointer it first come to will cause crash. even I want to get the port name or port address.
myWidget::myWidget(QWidget *parent) : QWidget(parent), m_ui(new Ui::Form)
{ status = 0;
m_ui->setupUi(this);
m_ui->statusIndicator->status = &status; // status in ui pointing to null before this
m_ui->turnOffButton->setChecked(true);
connect(m_ui->turnOnButton, SIGNAL(clicked(bool)), this, SLOT(switchStatus()));
//crashes when click ratioButton,
//compare to last line in the mainwindow construtor
connect(m_ui->turnOffButton, SIGNAL(clicked(bool)), this, SLOT(switchStatus()));
}
void myWidget::switchStatus()
{
qDebug() << "swithcing";
if(status)
{
setStatus(false);
}
else
{
setStatus(true);
}
m_relay->switchStatus();
}
void relay::switchStatus()
{ if(status) turnOff();
else turnOn();
}
bool relay::turnOn(){
qDebug() << writeDataOn; // test if string is correct
qDebug() << serialPort; // crashes whenever serialPort address is called
const qint64 bytesWritten = serialPort->write(writeDataOn);
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
qDebug() << infos[0].description();
QSerialPort serialPort;
serialPort.setPortName(infos[0].portName());
serialPort.setBaudRate(QSerialPort::Baud9600);
if (!serialPort.open(QIODevice::ReadWrite)) {
qDebug() << QObject::tr("Failed to open port %1, error: %2").arg(serialPort.portName()).arg(serialPort.error()) ;
}
ui->setupUi(this);
for(int i=0;i<4;i++)
{
relays[i].setRelayNumber(i);
relays[i].setPort(&serialPort);
relays[i].setStatus(relays[i].getRealStatus());
}
ui->widget->m_relay = relays;
qDebug() << ui->widget->m_relay;
qDebug() << ui->widget->m_relay->getPort();
ui->widget->switchStatus(); // this line runs well
}
Make your serial port a member of the MainWindow class. At the moment it is local to your constructor, so it is deallocated when the constructor finishes, hence the crashing. If you have declared a 'serialPort' member, you are hiding it by redeclaring it as a local scope variable inside the constructor.
Related
I tried maybe everything but I just don't see a mistake in this code. I made two windows inherited from QMainWindow: one with qt designer and another just in constructor. I'm trying to switch these windows in the MainWindow class depending on what buttons you press.
LoginWindow.cpp
LoginWindow::LoginWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::LoginWindow) {
ui->setupUi(this);
}
LoginWindow::~LoginWindow() {
delete ui;
}
void LoginWindow::on_confirmButton_clicked() {
emit confirmButton_clicked();
}
RegistrationWindow.hpp
RegistrationWindow::RegistrationWindow(QWidget* parent): QMainWindow(parent) {
label = new QLabel("Fast Typing"); //variable in .hpp
label->setAlignment(Qt::AlignCenter);
label->setFont(QFont("Lucida Console", 12, 2));
QVBoxLayout * l = new QVBoxLayout();
l->addWidget(label);
l->addWidget(new QLabel("Login", this));
l->addWidget(new QLineEdit(this));
l->addWidget(new QLabel("Password", this));
l->addWidget(new QLineEdit(this));
p = new QPushButton("Confirm", this); //variable in .hpp
l->addWidget(p);
w = new QWidget(); //variable in .hpp
w->setLayout(l);
setCentralWidget(w);
connect(p, &QPushButton::clicked, this, &RegistrationWindow::on_confirmButton_clicked);
}
void RegistrationWindow::on_confirmButton_clicked() {
emit confirmButton_clicked();
}
RegistrationWindow::~RegistrationWindow() { }
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
//variables in .hpp
logWindow = new LoginWindow();
regWindow = new RegistrationWindow();
goToReg();
connect(logWindow, SIGNAL(confirmButton_clicked()), this, SLOT(goToReg()));
connect(regWindow, SIGNAL(confirmButton_clicked()), this, SLOT(goToLog()));
}
void MainWindow::goToReg() {
qDebug() << "goToReg";
resize(regWindow->size());
qDebug() << "goToReg";
setCentralWidget(regWindow);
setWindowTitle("Registration");
qDebug() << "registration: " << regWindow;
qDebug() << "login: " << logWindow;
}
void MainWindow::goToLog() {
qDebug() << "goToLog";
resize(logWindow->size());
qDebug() << "goToLog";
setCentralWidget(logWindow);
setWindowTitle("Login");
qDebug() << "registration: " << regWindow;
qDebug() << "login: " << logWindow;
}
All I could say is it switches windows no matter what I make a starting window and for second time it's crashes on the line setCentralWidget() in slot with SEGV runtime error.
From the documentation of QMainWindow::setCentralWidget:
Note: QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time.
The widgets that logWindow and regWindow point to start life unowned. Then you call goToReg(), which calls setCentralWidget(regWindow) - now the main window owns *regWindow. Then you call goToLog(), which calls setCentralWidget(logWindow) - now the main window takes ownership of *logWindow and destroys *regWindow, so regWindow becomes a dangling pointer. Next time you attempt to use it, the program exhibits undefined behavior.
I successfully read from a serial port with QSerialPort when I setup the instance's parameters in my main window constructor. I want my GUI to enable changing port however, but I can't seem to start reading when changing my com port after starting my application (I do so with a spinBox that sets up the COM port name of my QSerialPort instance). Here is my code (mainwindow.cpp), my problem is that if I do not directly use serial->setPortName(); in my constructor, and I put it in my slot linked to my spinBox signal, I can't read data received from my com port with qDebug anymore.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDebug>
QSerialPort *serial;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this); // Here by default. Takes a pointer to mainwindow as argument
serial = new QSerialPort(this);
qDebug() << "nb ports: " << QSerialPortInfo::availablePorts().length();
foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts())
{
qDebug() << "name" << serialPortInfo.portName();
}
qDebug() << "is " << serial->open(QSerialPort::ReadOnly);
qDebug() << "err " << serial->error();
// Create the signal and slot for
connect(ui->com_spinBox, SIGNAL(valueChanged(const QString&)),
this, SLOT(setComPort(const QString&)));
// Create the signal and slot for receiving data from device
connect(serial, SIGNAL(readyRead()), this, SLOT(serialReceived()));
}
MainWindow::~MainWindow()
{
delete ui;
serial->close(); // instance is closed when mainwindow destroyed
}
// My 2 custom slots below!!!
void MainWindow::serialReceived()
{
QByteArray ba;
ba = serial->readAll();
ui->label->setText(serial->readAll());
qDebug()<<ba;
}
void MainWindow::setComPort(const QString& com)
{
serial->close();
serial = new QSerialPort(this); // this (mainwindow) is parent
qDebug() << serial->portName();
QString comPort = "COM" + com;
qDebug() << comPort;
serial->setPortName(comPort);
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
serial->open(QSerialPort::ReadOnly);
}
Here I tried moving all my serial instance setup from constructor to my slot, and close and re-open the instance to see if it helps, but I still cannot read anything with serialReceived() slot when tuning to the right COM port.. It does work if I put everything in the constructor and setPortName() with the right port number at the start of the program.
Thanks!
Try to delete and create again your QSerialPort before setPortName()
serial->deletLater();
serial = new QSerialPort(this);
serial->setPortName("COM1");
I own a serial communication class and I want to send a signal when I receive a commend because the data reception is over. As a result of this signal, I'm calling a graphical method. The connection is established, but the receiving slot does not listen to the signal.
singleton class:
class SerialCommunication : public QObject{
Q_OBJECT public:
static SerialCommunication & GetInstance() ;
....
QVector<double> ReadDataVector ;
private:
SerialCommunication() ;
static SerialCommunication * Instance ;
QList<QSerialPortInfo> PortList ;
bool IsOpen = false ; ....
private slots:
void ReceivedData() ;
signals:
void DataIsDone() ;
};
ReceivedData slot :
void SerialCommunication::ReceivedData()
{
...
for(;true;)
{
....
switch (Commend) {
case Data_Reply:
qDebug()<<"Data Reply" ;
AppendToUint16Vector(ReadDataVector,Package);
break;
case Data_Reply_Done:
qDebug()<<" Data_Reply_Done" ;
MsgBox.information(0,"Transfer information. ","The data transfer is complete \n Please click draw button.");
emit SerialCommunication::GetInstance().DataIsDone();
break;
default:
ClearBuffer();
qDebug()<<" default" ;
break;
}}}
and connection is here
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&SerialCommunication::GetInstance(),SIGNAL(SerialCommunication::DataIsDone),this,SLOT( GraficSetupAndInit()));
SetupUI();
}
QObject::connect: Parentheses expected, signal SerialCommunication::SerialCommunication::DataIsDone in ..\Muteferrika\MainWindow.cpp:10
QObject::connect: (receiver name: 'MainWindow')
Using the old Qt4 syntax your connect call should be...
connect(&SerialCommunication::GetInstance(), SIGNAL(DataIsDone()),
this, SLOT(GraficSetupAndInit()));
Having said that you really should be using the newer signal-slot syntax available with Qt5. Specifically...
connect(&SerialCommunication::GetInstance(), &SerialCommunication::DataIsDone,
this, &MainWindow::GraficSetupAndInit);
This is my first time to ask on Stack Overflow. If any suggestion about asking question, please let me know.
I am very new to Qt, and have some problems while using event. Hope someone can provide any thoughts.
Background:
I have my custom pushButton, which is rxPushButton in rx.h and rx.cpp. I use on_rxPushButton_clicked to change the image and it works pretty well.
In MainWindow, I need to use some rx so I include the class rx and I want to detect if I press left button of the mouse, I need to know which rx has been pressed and record its id in int rxId in MainWindow.
Problem:
I tried two ways to achieve my goal, including mousePressEvent and eventFilter. I found that I can't detect the mouse pressed signal on any rx, but I can detect it outside rx in other places in Mainwindow. I wonder if the events will conflict, but when I comment on_rxPushButton_clicked in rx.cpp, MainWindow still doesn't work for the problem. So I presume that maybe the space in the screen occupied by rx will not be in control of MainWindow (I can get Debug message "test1" but not "test2" in my code, check below).
How should I do to solve this problem if I need both things (change image in rx and modify one variable in MainWindow)? Or maybe it's just something wrong with my code and how to modify?
I hope to separate them if possible because I still need to include many objects in MainWindow in the future.
Here are some of my related codes:
rx.cpp
void rx::on_rxPushButton_clicked(void)
{
startLoading();
}
void rx::startLoading(void)
{
state = 1;
connect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
timer->start(LOADING_INTERVAL);
}
void rx::loading1(void)
{
if(state == 1)
{
state = 2;
ui->rxPushButton->setStyleSheet("border-image: url(:/images/Rx/Rxloading1.png);");
disconnect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
}
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
}
//rxList[0]->installEventFilter(this);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "test1";
if(rxList[0]->rect().contains(event->x(), event->y()))
qDebug() << "test2";
}
}
Change image in rx
Use QSignalMapper class that bundles signals from QWidgets (class rx) and re-emits them with widget (class rx) parameters corresponding to the object that sent the signal.
We connect each button's clicked() signal to the signal mapper's map() slot, and create a mapping in the signal mapper from each button to the button itself.
Finally we connect the signal mapper's mapped() signal to the custom widget's(rx object) clicked() signal. When the user clicks a button, the custom widget(rx object) will emit a single clicked() signal whose argument is the button (rx object) the user clicked.
Handle the button clicked event in MainWindow::slotButtonsClicked(QWidget *p) function.
Modify n variable in MainWindow:
You can update variable of MainWindow in mousePressEvent function
class MainWindow : public QWidget
{
public :
MainWindow (QWidget *parent);
QSignalMapper * mapper;
slots:
void slotButtonsClicked(QWidget *);
// ...
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mapper = new QSignalMapper(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
QObject::connect(rxList[i], SIGNAL(clicked()),mapper,SLOT(map()));
//Adds a mapping so that when map() is signalled from the sender, the signal mapped(widget ) is emitted.
mapper->setMapping(rxList[i], rxList[i]);
}
QObject::connect(mapper,SIGNAL(mapped(QWidget *)),this,SLOT(slotButtonsClicked(QWidget *)));
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "Clicked outside the button";
//Now you can modify n variable in MainWindow
}
MainWindow::mousePressEvent(event);
}
void MainWindow::slotButtonsClicked(QWidget *p)
{
rx *button = dynamic_cast<rx *>(p);
button->on_rxPushButton_clicked();
qDebug() << button <<" clicked on button";
//Now you can change image in rx
}
Hope this works :)
Event propagating prevent you to do it. When mouse is over Button, event sends to button, not to the MainWindow, thats why you never see test2 in debug output.
Since I don't know what do you need it's hard to say what you should do. You can process event in Button and send some signals or whatever, or set event filter and check if target object is your button, and so on.
Any way, read Qt event system docs for better understanding.
I have an LCDNumber display panel in QT. I want to update the value of it continuously with a variable being received from an external servo motor (the speed)
I have the following code
HANDLE RS232Handle;
UCHAR Address = 0;
UCHAR Status = 0;
int Value = 0;
GetResult(RS232Handle, &Address, &Status, &Value);
printf("Result: Address=%d, Status=%d, Value=%d\n", Address, Status, Value);
ui->lcdNumber_TarRot_Status->display(Value);
All these lines must run to get the proper value. I have looked into calling a function every x seconds, and I have tried a for loop that runs forever, but nothing really works as desired. Is there a proper way of doing this?
Thanks!
I don't know how you tried to "calling a function every x seconds" - most likely you used a blocking wait to do so. Instead, call it from a timer, without blocking the event loop.
class MyClass : public QWidget {
Q_OBJECT
Ui::MyClass ui;
HANDLE m_device = 0;
QBasicTimer m_queryTimer;
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == m_queryTimer.timerId())
queryDevice();
}
void queryDevice() {
UCHAR address = 0;
UCHAR status = 0;
int value = 0;
GetResult(m_device, &address, &status, &value);
qDebug() << "Result: Address" << address << "Status" << status << "Value" << value;
ui->lcdNumber_TarRot_Status->display(value);
}
}
...
public:
explicit MyClass(QObject *parent = nullptr) : QObject(parent) {
ui.setupUi(this);
m_queryTimer.start(1000, this);
...
}
void openDevice() {
...
m_device = ...;
}
};
I ended up using a QTimer since I'm working with QT:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateMCvalues()));
timer->start();
}
void MainWindow::updateMCvalues() {
HANDLE RS232Handle;
UCHAR Address = 0;
UCHAR Status = 0;
int Value = 0;
GetResult(RS232Handle, &Address, &Status, &Value);
printf("Result: Address=%d, Status=%d, Value=%d\n", Address, Status, Value);
ui->lcdNumber_TarRot_Status->display(Value);
}
From the Qt docs on the QLCDNumber class. display() is a slot, not a public function. Calling it directly won't work unless on the UI thread. See here for info on signals and slots if you are unfamiliar.
The proper usage would be to connect a signal of your choosing (i.e. make your own) to the ````display()``` slot.
Say you made a signal called output_number, once connected to the display() slot. You could call:
emit output_number(Value);
Which would in turn call the display slot on the Qt UI thread.