I have a modal dialog called by:
qDebug() << "Creating LoginStatusDialog";
dlgConnectStatus = new LoginStatusDialog(login, key, auth);
qDebug() << "Done LoginStatusDialog, setting modal";
dlgConnectStatus->setModal(true);
qDebug() << "Done setting modal, executing";
int res = dlgConnectStatus->exec();
qDebug() << "dlgConnectStatus result = " << QString::number(res);
//see below for debug info and output
This calls a custom constructor which executes this below
LoginStatusDialog.cpp (extract):
LoginStatusDialog::LoginStatusDialog( QString _login, QString _key, QString *_auth_tok, QWidget *parent) :
QDialog(parent), ui(new Ui::LoginStatusDialog), login(_login), key(_key)
{
ui->setupUi(this);
Return_Object = new ReturnObject(ReturnCode::netcon_LoginSuccess, QString(""));
if (Return_Object->getCode() == ReturnCode::netcon_LoginSuccess) {
qDebug() << "pre close";
close();
//accept - hangs
//done(0); - hangs
qDebug() << "post close";
}
}
LoginStatusDialog header
#ifndef LOGINSTATUSDIALOG_H
#define LOGINSTATUSDIALOG_H
#include <QDialog>
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <thread>
#include "returnobject.h"
#include "datamanager.h"
namespace Ui {
class LoginStatusDialog;
}
class LoginStatusDialog : public QDialog
{
Q_OBJECT
public:
// explicit LoginStatusDialog(QWidget *parent = 0);
LoginStatusDialog( QString _login, QString _key, QString *_auth_tok, QWidget *parent = 0);
~LoginStatusDialog();
private:
Ui::LoginStatusDialog *ui;
QString login, key;
ReturnObject *Return_Object;
void initGui();
};
#endif // LOGINSTATUSDIALOG_H
Debug info
Debugging starts
Creating LoginStatusDialog
pre close
post close
Done LoginStatusDialog, setting modal
Done setting modal, executing
//remains open
By doing some research, several SO posts refer to using done(), close(), accept() to close the dialog
Using a CONNECT() uses the close() method to close the dialog, but attempting to manually close it has no success.
It was suggested to use a timer task, but that just does not seem like the actual way to close the dialog.
Any thoughts?
This seems more like a general design issue. If you truly want it done that way you could use a timer. But a simple redesign may be more helpful.
If you only want the dialog to be shown when an error occurs why not only show it only then?
Return_Object = new ReturnObject(ReturnCode::netcon_LoginSuccess, QString(""));
if (Return_Object->getCode() != ReturnCode::netcon_LoginSuccess)
{
dlgConnectStatus = new LoginStatusDialog(Return_Object);
dlgConnectStatus->setModal(true);
int res = dlgConnectStatus->exec();
//...
}
This way, the dialog who is responsible for only showing the information will only do this. Show the login error. It just seems like a design flaw for creating a dialog then immediately closing it (especially considering that the good case should be your default case).
If you have extra things the Dialog is doing you should consider how much of that code really needs to be in a class intended to be for showing information.
Calling close from constructor is wrong, cause your dialog is showing after constructor ends, so your check can't close dialog, that wasn't showed. You need to call close after(in) QDialog::showEvent. Or, just as I say in your first theme - you can use timer loop.
Related
My main application class is USB_Packet_Analyzer which processes some files, but also supports reading from a file to which somebody still writes. In that case, I have an infinity loop which checks whether something new was written in this file and if so, I continue in processing. In every iteration of loop I am calling QCoreApplication::processEvents();.
The problem is that when I close main window of app, it wont stop the application. If I check QApplication::topLevelWidgets().size() it is still 1, and QApplication::topLevelWidgets().at(0)->isVisible() is false. Why isn't it closing my window? Can I connect some signal to detect whether red cross on window was clicked or not? I know that when getting into this kind of loops i might use QThread, but I'd rather not get this involved. Is there any solution for my problem?
Take a look at the QFileSystemWatcher Class and its fileChanged signal.
#include <QFileSystemWatcher>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private:
QFileSystemWatcher* fileWatcher;
Ui::MainWindow*ui;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
createUi();
fileWatcher = new QFileSystemWatcher(this);
connect(fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChangedSlot(QString)));
QFileInfo file("file_to_watch.txt");
if (file.exists())
{
fileWatcher->addPath(file.absoluteFilePath());
}
}
void MainWindow::fileChangedSlot(const QString &path)
{
if (fileWatcher->files().contains(path))
{
qDebug() << "File changed" << path;
}
}
You can using of system interrupt signal to manage a Boolean to breaking your infinite loop or exiting the application.
e.g:
#include <iostream>
#include <csignal>
using namespace std;
void InterruptSignalHandler(int signal_number) {
cout<< "Signal Number is "<<signal_number<<endl;
exit(signal_number);
}
int main()
{
signal(SIGINT, InterruptSignalHandler);
string a;
while(true)
{
cout<<">";
cin >> a;
}
return 0;
}
I have issue with reading and writing from/to QSerialPort. I read what I have written. Here is some example code that proves that. Maybe I need to write and read from different channels. Code's provided below thx in advance. Provided log of the program with image.
Log of the program here
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QTimer>
#include <QDebug>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QSerialPort *serialPort;
QTimer* sendTimer;
QByteArray receiveDataBuffer;
private slots:
void onReadyRead();
void onSendDataTimeout();
};
#endif // MAINWINDOW_H
MainWindows.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
serialPort(new QSerialPort),
sendTimer(new QTimer)
{
sendTimer->setInterval(1000);
serialPort->setPortName("COM5");
serialPort->setBaudRate(QSerialPort::Baud115200);
serialPort->setParity(QSerialPort::Parity::NoParity);
serialPort->setDataBits(QSerialPort::DataBits::Data8);
serialPort->setStopBits(QSerialPort::StopBits::TwoStop);
serialPort->open(QSerialPort::OpenModeFlag::ReadWrite);
connect(serialPort,&QSerialPort::readyRead,this,&MainWindow::onReadyRead);
connect(sendTimer,&QTimer::timeout,this,&MainWindow::onSendDataTimeout);
sendTimer->start();
}
MainWindow::~MainWindow()
{
}
void MainWindow::onReadyRead()
{
receiveDataBuffer.append(serialPort->readAll());
if (receiveDataBuffer.contains('^') && receiveDataBuffer.contains('$') && receiveDataBuffer.lastIndexOf('$') < receiveDataBuffer.lastIndexOf('^')) {
QByteArray extractedByteArray;
for(int i = receiveDataBuffer.lastIndexOf("$") ; i < receiveDataBuffer.lastIndexOf("^") + 1 ; i++){
extractedByteArray.append(receiveDataBuffer[i]);
}
qDebug()<<"Received:"<<extractedByteArray<<endl;
receiveDataBuffer.clear();
}}
void MainWindow::onSendDataTimeout()
{
qint64 result = serialPort->write("$10,0,123,123^");
bool flush = serialPort->flush();
result != -1 && flush ? qDebug()<<"Data sent"<<endl:qDebug()<<"Failed to send data"<<endl;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
First of all, try to connect the different signals that are emitted when you read/write that from your port.
For instance to check that the data has been sent properly, according to the documentation you can use the signal QIODevice::bytesWritten:
The signal is emitted every time a payload of data has been written to the device's current write channel. The bytes argument is set to the number of bytes that were written in this payload.
connect(serialPort,&QSerialPort::bytesWritte, this, [](const qint64 bytes) {
qDebug() << "Sent data: " << bytes << " bytes.";
});
To read the data, as you already did, you need to check the QIODevice::readyRead signal:
This signal is emitted once every time new data is available for reading from the device's current read channel. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
Regarding the problem you described, the first step to do is to ensure that your decive has not been configured to work in echo-mode. To turn it off, you will need to check your device interface.
If this is not the problem, check the available ports by using `QSerialPort::availablePorts and ensure that you are connecting to the right one. For example, to connect to the first port, you can do something like this and check that the configuration is compatible with your device:
auto serialInfo = QSerialPortInfo::availablePorts();
serialPort->setPort(serialInfo[0])
auto ret = serialPort.open(QSerialPort::ReadWrite)
&& serialPort.setBaudRate(QSerialPort::Baud38400)
&& serialPort.setDataBits(QSerialPort::Data8)
&& serialPort.setStopBits(QSerialPort::OneStop)
&& serialPort.setParity(QSerialPort::NoParity);
qDebug() << "Port has been configured properly?: " << ret;
If the problem persist, you can get the native handler and modify its properties by calling QSerailPort::handle().
If the platform is supported and the serial port is open, returns the native serial port handle; otherwise returns -1.
The last option, it does not work in all platforms. Worse scenario, take a look into the official docs of termios. It provides a low-level interface that allows you to enable/disable the echo mode.
struct termios options;
tcgetattr(file, &options);
cfmakeraw(&options);
options.c_lflag &= ~(ECHO | ECHOE); // Add or disable the flags
tcsetattr(file, TCSANOW, &options);
The problem was in
serialPort->setStopBits(QSerialPort::StopBits::TwoStop);
The other device was connected to serial port with QSerialPort::StopBits::OneStop.
So changing the stop bits serialPort->setStopBits(QSerialPort::StopBits::OneStop);
solved the problem and there were no echoes anymore.Thx.
In my code I would like to integrate an auto-save function that runs every couple seconds or so. I would like this to run in the background because I have other stuff that I am going to be running at the same time. So how would I do this?
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <fstream>
#include <QFile>
#include <QDebug>
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
// Setup code
ui->textEdit->setReadOnly(true);
ui->textEdit->append("Select one of the buttons on the left to pick a log");
}
MainWindow::~MainWindow() {
delete ui;
}
string lastSavedText[] = {
" ",
" "
};
QString qLastSavedTextHome, qLastSavedTextWork;
This is my first button
void MainWindow::on_homeButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file { "home.apl" };
if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file.readAll();
file.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is my second one
void MainWindow::on_workButton_clicked() {
// Preparing text edit
ui->textEdit->setReadOnly(false);
ui->textEdit->clear();
ui->textEdit->setOverwriteMode(true);
// Loading previously saved text
QFile file2 { "work.apl" };
if ( !file2.open(QIODevice::ReadOnly | QIODevice::Text) ) {
qDebug() << "Could not open file!";
return;
}
const auto& lastSavedText = file2.readAll();
file2.close();
ui->textEdit->setPlainText( lastSavedText );
}
This is the save button I hope to eliminate with an autosave
void MainWindow::on_saveButton_clicked() {
// Converts textEdit to string
QString textEditText = ui->textEdit->toPlainText();
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
There is 2 solutions.
Easy one
Use simply a timer that will execute the code of your save button. You can set the timer to execute any period of time.
QTimer
But this might cause the software to freeze if this operation takes too much time. In which case, you can put the function that saves inside a thread.
Threads
You can use threads to do that.
Thread, is basically a process that will detach from your main process and can be run at the same time, each thread doing its own work.
Note that to communicate between thread, the safest method is to use signals.
Qt Threads Documentation
Example
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();
}
You can use a QTimer with QtConcurrent::run, and then you get the simplicity with the benefit of running the saving on a different thread you don't need to manage.
Practically, try
QTimer::singleShot(time, this, Qt::TimerType::CoarseTime, QtConcurrent::run(this,&MainWindow::on_saveButton_clicked));
Here's a first approximation using a background thread (for the sake of brevity, it inherits QThread - for your real application, consider decoupling the QThread base-class from this worker thread object. That will also make it possible to give a father-object for t).
class Thread: public QThread {
Q_OBJECT
public:
Thread(QTextEdit *textEdit):textEdit(textEdit) {
QTimer *t = new QTimer;
connect(t, SIGNAL(timeout()), SLOT(saveOnce()));
t->moveToThread(this);
t->start(2000);
}
protected:
QTextEdit *textEdit;
std::string lastSavedText[2];
private slots:
QString text() const { return textEdit->toPlainText(); }
void saveOnce() {
QString textEditText;
QMetaObject::invokeMethod(this,
"text", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString,textEditText));
lastSavedText[0] = textEditText.toStdString();
// Saving files
ofstream home;
home.open("home.apl");
home << lastSavedText[0];
home.close();
ofstream work;
work.open("work.apl");
work << lastSavedText[1];
work.close();
}
};
Care must be taken, when taking this approach with BlockingQueuedConnection, that the thread does not call invokeMethod while the main thread is waiting for it to exit - then a deadlock happens because the main-thread cannot process the text() queued call anymore.
I am aware of the applications and use of modal dialogs(QDialog) and examples given all around the web, but my use is for displaying progress.
Have Tried:
As per threads/posts from here and here, I have found to use accept(), done(), close(), setResult() but none of these close the dialog, the dialog just hangs.
I have a connect() to "click close" but this does not fully satisfy the requirements, since a successful or unsuccesful execution will in either case await user input.
update: just tried setResult(Accepted);,QDialog remains hanging, even after successful execution.
Problem:
The modal dialog is used to show progress, at which the end of a succesful execution should close the modal dialog automatically, but if not executed successfully, the dialog should remain open for the user to view the displayed error.
Used functions:
I have used these above proposed functions. These do not close the dialog, the dialog just hangs awaiting user interaction (i.e. manaully closing it with a slot from a button)
CODE:
The dialog is executed with:
dlgConnectStatus = new LoginStatusDialog(login, key, auth);
dlgConnectStatus->setModal(true);
int res = dlgConnectStatus->exec(); << see note below
qDebug() << "dlgConnectStatus result = " << QString::number(res);
note: all code running the execution is in the dlgConnectStatus class contructor. This code is only executed once the dlgConnectStatus->exec(); is called, however no result is ever returned, the code finshes executing and hangs after the last constructor line.
I attempted adding :
if (Return_Object->getCode() == ReturnCode::netcon_LoginSuccess) {
setResult(Accepted);
accept();
}
but it still just hung!
*Finishing info (irrelevant but for putting into context):
After a code execution (successful or not), a public object is extracted from the dlgConnectStatus object before being destroyed.*
TL;DR
How can I close a QDialog, type modal, manually?
Bare/Essential Code for creating the issue
Calling Code:
dlgConnectStatus = new LoginStatusDialog(login, key, auth);
qDebug() << "Done LoginStatusDialog, setting modal";
dlgConnectStatus->setModal(true);
qDebug() << "Done setting modal, executing";
int res = dlgConnectStatus->exec();
qDebug() << "dlgConnectStatus result = " << QString::number(res);
//see below for debugger output -> the qDebug output
LoginStatusDialog.h
#ifndef LOGINSTATUSDIALOG_H
#define LOGINSTATUSDIALOG_H
#include <QDialog>
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <thread>
#include "returnobject.h"
#include "datamanager.h"
namespace Ui {
class LoginStatusDialog;
}
class LoginStatusDialog : public QDialog
{
Q_OBJECT
public:
// explicit LoginStatusDialog(QWidget *parent = 0);
LoginStatusDialog( QString _login, QString _key, QString *_auth_tok, QWidget *parent = 0);
~LoginStatusDialog();
private:
Ui::LoginStatusDialog *ui;
QString login, key;
ReturnObject *Return_Object;
void initGui();
};
#endif // LOGINSTATUSDIALOG_H
LoginStatusDialog.cpp
//custom constructor
LoginStatusDialog::LoginStatusDialog( QString _login, QString _key, QString *_auth_tok, QWidget *parent) :
QDialog(parent), ui(new Ui::LoginStatusDialog), login(_login), key(_key)
{
ui->setupUi(this);
//this code is basically self explanitory
Return_Object = new ReturnObject(ReturnCode::netcon_LoginSuccess, QString(""));
if (Return_Object->getCode() == ReturnCode::netcon_LoginSuccess) {
//by use ofbreakpoints, I can verify this close is reached but not executed.
qDebug() << "pre close";
close();
qDebug() << "post close";
}
}
Debug info
Debugging starts
Creating LoginStatusDialog
pre close
post close
Done LoginStatusDialog, setting modal
Done setting modal, executing
//remains open
Your check with close is doing in constructor, but exec is calling after it, so u need do something like timer in constructor and connect it's timeout signal with slot, that will contain your login check. Also, you can reimplement exec method for starting timer only after exec called.
I'm writing a Qt (5.3) program which has a joystick test UI in it, but I need a separate thread for an infinite while loop looking for joystick axis/button value changes through SDL. That part of the code is working fine as I can have the thread qDebug() messages and it seems to work. But from the main window, when I try to open the test joystick UI, the program crashes. I've had the test joystick UI running separation WITHOUT the JoystickThread thread and it seems to open up fine.
The error messages are inconsistent though - some times, I just get
The program has unexpectedly finished.
/home/narendran/QtWorkspace/build-LinkControl-Desktop-Debug/LinkControl crashed
This has shown up once:
QXcbWindow: Unhandled client message: "_GTK_LOAD_ICONTHEMES"
And a few other times:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
star: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
I found that this was common if XInitThreads(); is not run in the main function, but even with it on there, it crashes with the same error(s).
main.cpp
#include <qsplashscreen.h>
#include "linkcontrol.h"
#include "configure.h"
#include <unistd.h>
#include <QApplication>
#include <QPixmap>
#include <QStyle>
#include <QDesktopWidget>
#include "linkports.h"
#include "joystickthread.h"
#include <X11/Xlib.h>
int main(int argc, char *argv[])
{
XInitThreads();
QApplication a(argc, argv);
QPixmap pix(":splash.png");
QSplashScreen splash(pix);
splash.show();
a.processEvents();
JoystickThread jsThread;
jsThread.start();
LinkControl linkcontrol;
usleep(1000000);
splash.finish(&linkcontrol);
usleep(100000);
linkcontrol.show();
linkcontrol.setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter,linkcontrol.size(),a.desktop()->availableGeometry()));
return a.exec();
}
The actual thread is in joystickthread.cpp
#include "joystickthread.h"
#include "global.h"
#include "unistd.h"
/* JoystickThread::JoystickThread(int _interval)
{
this->interval_us = _interval;
} */
void JoystickThread::run()
{
while(1)
{
if(joystick->connected)
{
joystick->updateJSData();
// Check for changed values
for(int i=0; i<joystick->axis.count(); i++)
{
if(joystick->axis.value(i) != joystick->axis_last[i])
{
joystick->axisUpdateEmit(i);
// qDebug() << "AXIS: " << i << "\tVALUE: " << joystick->axis.value(i);
}
joystick->axis_last[i] = joystick->axis.value(i);
}
for(int i=0; i<joystick->button.count(); i++)
{
if(joystick->button.value(i) != joystick->button_last[i])
{
joystick->btnUpdateEmit(i);
// qDebug() << "BUTTON: " << i << "\tVALUE: " << joystick->button.value(i);
}
joystick->button_last[i] = joystick->button.value(i);
}
}
usleep(2500);
}
}
The function that causes the program to crash is in linkcontrol.cpp
void LinkControl::on_actionJoystick_Test_triggered()
{
qDebug() << "STARTING CHECK";
if(!js_test->initialized) {
qDebug() << "NOT INIT";
js_test = new TestJoystick();
js_test->initialized = true;
qDebug() << "FININSH INIT";
}
if(joystick->connected) {
qDebug() << "SHOWING UI";
js_test->show();
} else {
QMessageBox::critical(this, tr("No Joystick Connected!"), tr("Please connect a joystick first..."));
}
}
Where js_test is declared as a TestJoystick object in the linkcontrol.h file
public:
explicit LinkControl(QWidget *parent = 0);
QSlider *portSliders[16];
QLineEdit *setVals[16];
SerialTerminal *ser_term;
TestJoystick *js_test;
~LinkControl();
Thank you very much! Please let me know if you need anymore information.
QThreads are a little tricky to get used to initially, and have their share of gotchas.
You should construct and connect appropriate items at the top of your run function.
If you do it other places, you need to make sure that you don't use Qt::AutoConnection, but instead use Qt:QueuedConnection.
http://qt-project.org/doc/qt-5/qt.html#ConnectionType-enum
Certain elements are only accessible from the "GUI" thread or the main thread of the program. This is the thread that has QApplication::exec(); ran on. It has an event loop that propagates messages around.
Look at the Application output for runtime errors that Qt will tell you about.
When crossing thread boundaries, be sure to use signals and slots.
And if you are accessing a member of your thread class from outside that thread, be sure to use thread synchronization, practices, such as prefacing all access to these members with QMutexLocker locker(m_mutex);.
http://qt-project.org/doc/qt-5/threads.html
And as implied by the title "GUI thread", it is the only thread that is allowed to do certain things such as drawing QPixmaps and accessing certain parts of QWidgets.
Hope that helps.