I have an app with a splash screen that I keep up for 2 seconds using the thread sleep method. In my MianWindow I have a object that listens to the sslErrors signal then calls a slot that displays the ssl error. I don't want that dialog with the errors displaying until after the splash screen has closed. This application has to also be able to run from a network folder.
On my local machine it behaves the way I want it to everytime but on a network drive sometimes the SSL error dialog opens while the splash screen is displayed, so when I click the No button on the dialog (for ignoring the errors) my app crashes :S
Main.cpp
#include "mainwindow.h"
#include "single_application.h"
#include <QtCore>
#include <QApplication>
#include <QSplashScreen>
#include <QStringList>
#include <QMessageBox>
const QString CSM_VERSION = QString("v1.0i");
class MyInitThread : public QThread
{
protected:
void run(void)
{
this->sleep(2);
}
};
int main(int argc, char *argv[]) try
{
bool my_global_bool = true;
SingleApplication a(argc, argv, "CSM_CYTO_VIEWER_RUN_ONCE");
a.setProperty("my_global_bool", my_global_bool);
if (a.isRunning())
{
QMessageBox::information(0, "Cyto Viewer " + CSM_VERSION, "Cyto Viewer is already running.");
return 0;
}
//QApplication a(argc, argv);
QApplication::setWindowIcon(QIcon(":/logo/csm.ico"));
QApplication::setApplicationName(QString("Cyto Viewer"));
QApplication::setApplicationVersion(QString(CSM_VERSION));
//Check if system has a system tray
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
QMessageBox::critical(0, QObject::tr("Systray"),
QObject::tr("I couldn't detect any system tray "
"on this system."));
return 1;
}
Q_ASSERT( QSslSocket::supportsSsl() );
MainWindow w;
QPixmap pixmap(":/splash/splash.png");
QSplashScreen splash(pixmap,Qt::WindowStaysOnTopHint);
w.setWindowOpacity(0);
w.setWindowTitle("Cyto Viewer " + CSM_VERSION);
splash.show();
QEventLoop loop;
MyInitThread *thread = new MyInitThread();
QObject::connect(thread, SIGNAL(finished()), &loop, SLOT(quit()));
QObject::connect(thread, SIGNAL(finished()), &splash, SLOT(close()));
QObject::connect(thread, SIGNAL(finished()), &w, SLOT(slInit()));
thread->start();
loop.exec();
w.show();
return a.exec();
}
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(view->page()->networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )),
this, SLOT(onSslErrors(QNetworkReply*, const QList<QSslError> & )));
}
SSL errors slot
void MainWindow::onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors)
{
if(SSL_ONCE != true){
QString errorString;
bool firstError = false;
//TODO: Get this displaying cirtificate errors
foreach (const QSslError e, errors)
{
if(firstError != true) {
if (!errorString.isEmpty())
errorString += "";
if(e.error() != e.NoError){
errorString += "<ul style=\"background: url(minus-small.png);\"><li style=\"background: url(minus-small.png);\">" + e.errorString() + "</li></ul>";
}
}
}
//Delay MessageBox until the splash screen closes (2 seconds)
//delay(2.5);
QEventLoop loop;
MainWinThread *thread = new MainWinThread();
connect(thread, SIGNAL(finished()), &loop, SLOT(quit()));
//Detect any SSL errors from the network reply
thread->start();
loop.exec(); //Do event processing until the thread has finished!
QMessageBox sslQuestion;
//Fix for QMessageBox width
QSpacerItem horizontalSpacer(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
sslQuestion.setText("Cyto Viewer " + CSM_VERSION);
QGridLayout* layout = (QGridLayout*)sslQuestion.layout();
sslQuestion.setInformativeText("The following SSL error(s) occured: " + errorString + "Do you wish to continue?");
sslQuestion.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
sslQuestion.setIconPixmap(QPixmap(":/logo/lock-ssl.png"));
sslQuestion.setDefaultButton(QMessageBox::No);
sslQuestion.setWindowFlags(Qt::WindowSystemMenuHint | Qt::WindowTitleHint);
sslQuestion.setMinimumWidth(800);
layout->addItem(&horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
int SSLChoice = sslQuestion.exec();
if (SSLChoice == QMessageBox::No) {
reply->abort();
delete systray;
exit(EXIT_FAILURE);
//qApp->quit();
//return;
} else {
reply->ignoreSslErrors(/*errors*/);
SSL_ONCE = true;
}
} else {
reply->ignoreSslErrors();
return;
}
}
My slinit slot (for setting up system tray/hidingmain window)
void MainWindow::slInit()
{
setWindowOpacity(1);
//Set min size, this can be changed through registry
this->setMinimumSize(800, 640);
//System tray
aboutAction = new QAction("About", this);
quitAction = new QAction("Exit", this);
connect (aboutAction, SIGNAL(triggered()), this, SLOT(about()));
connect (quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
trayIconMenu = new QMenu(this);
trayIconMenu->addAction(aboutAction);
//trayIconMenu->addAction(printAction);
trayIconMenu->addAction(quitAction);
systray = new QSystemTrayIcon(this);
connect(systray, SIGNAL(messageClicked()), this, SLOT(messageClicked()));
QIcon icon(":/logo/csm.ico");
systray->setIcon(icon);
systray->setContextMenu (trayIconMenu);
systray->show();
if(systray->isVisible() /*&& !csmURL.isEmpty()*/){;
QString cdate = QDate::currentDate().toString("yyyy");
trayMsg = "<qt>Cyto Viewer "+CSM_VERSION+" is a browser produced by Perceptive Instruments© " + cdate + " specifically for Cyto Study Manager</b>.</qt>";
}
}
Related
I am trying to program a interface that has a push button and a menu bar using Qt 5.15.
Problem: The main issue is even though a menu bar (menuBar) is instantiated it is not visible in the .ui output window. Also, a push button (quitButton) is instantiated it is visible in the .ui output window. I might be missing something. I am getting a feeling that in Qt either you get a menu bar or a push button widgets but not both. Is it right? Moreover, how can I get both the menu bar and push button in the same .ui window?
This is my QtNotepad.cpp code
#include "qtnotepad.h"
QtNotepad::QtNotepad(QWidget *parent)
: QWidget(parent)
{
openAction = new QAction(tr("&Open"), this);
saveAction = new QAction(tr("&Save"), this);
exitAction = new QAction(tr("&Exit"), this);
connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
connect(exitAction, SIGNAL(triggered()), this, SLOT(quit()));
QMenuBar* menuBar = new QMenuBar(nullptr);
fileMenu = menuBar->addMenu(tr("&File"));
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);
fileMenu->addSeparator();
fileMenu->addAction(exitAction);
textEdit = new QTextEdit;
quitButton = new QPushButton(tr("Quit"));
connect(quitButton, SIGNAL(clicked()), this, SLOT(quit()));
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(textEdit);
layout->addWidget(quitButton);
setLayout(layout);
setWindowTitle(tr("Notepad"));
}
void QtNotepad::open()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", tr("Text Files (*.txt);; C++ Files(*.cpp *.h);;.dat Files(*.dat)"));
if (fileName != "")
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
{
QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
return;
}
QTextStream in(&file);
textEdit->setText(in.readAll());
file.close();
}
}
void QtNotepad::save()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), ""), tr("Text Files (*.txt);;C++ Files (*.cpp *.h)");
if (fileName != "")
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly))
{
//error message
}
else
{
QTextStream stream(&file);
stream << textEdit->toPlainText();
stream.flush();
file.close();
}
}
}
void QtNotepad::quit()
{
QMessageBox messageBox;
messageBox.setWindowTitle(tr("Notepad"));
messageBox.setText(tr("Do you really want to quit?"));
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
messageBox.setDefaultButton(QMessageBox::No);
if (messageBox.exec() == QMessageBox::Yes)
qApp->quit();
}
This is my QtNotepad.h code
#include <QtWidgets/QMainWindow>
#include "ui_qtnotepad.h"
#include<qtextedit.h>
#include<qpushbutton.h>
#include<qlayout.h>
#include<qobject.h>
#include<qmessagebox.h>
#include<qwidget.h>
#include<qdialog.h>
#include<qfiledialog.h>
#include<qtextstream.h>
#include<qmenubar.h>
class QtNotepad : public QWidget
{
Q_OBJECT
public:
QtNotepad(QWidget *parent = Q_NULLPTR);
private slots:
void quit();
void open();
void save();
private:
Ui::QtNotepadClass ui;
QTextEdit* textEdit;
QPushButton* quitButton;
QAction* openAction;
QAction* saveAction;
QAction* exitAction;
QMenu* fileMenu;
};
This is my main function code.
#include "qtnotepad.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtNotepad window;
window.show();
return a.exec();
}
This is my .ui output window.
QMenuBar* menuBar = new QMenuBar(nullptr);
This line may be the problem. There is one way to approach this -
Add a QMenuBar menuBar as a member of the class QtNotepad
Initialise the menu bar in constructor with parent as this, not nullptr
Since the menuBar is initialised with a nullptr parent, The Window will not take ownership of the menuBar.
I'm working on a computer vision project, an interactable surface that must handle user interactions through the video feed recognising the user's hands. The system recognizes the user's finger and map it on a GUI. I've developed the solution in Visual Studio 2017 using OpenCV for the video processing stuff, QT 5 to build the GUI and arduino to check the click on the surface. At the moment I've to controll a QTPushButton using the arduino serial output, that controll a boolean (isChecked) in the main class of the C++ program, I've read a lot about SIGNAL and SLOT in QT but when I trigger a SIGNAL using arduino serial connection and I handle the signal with connect() method in the MainWindow interface the system responds with the error : QObject::connect: No such signal QPushButton::valueChangedButton1(unsigned int).
The program failed to connect(button1, SIGNAL(valueChangedButton1(unsigned int)), this, SLOT(button1clicked()));, in the
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) instance.
SerialPort.h is an utility class to handle serial output from arduino. I've tried to put the connect() method in the while cycle of openCV but doesn't work.
MainWindow.h
#define NSAMPLES 7
#define ORIGCOL2COL COLOR_BGR2HLS
#define COL2ORIGCOL COLOR_HLS2BGR
#define PI 3.14159
#include <QtWidgets/QMainWindow>
#include <QtCore/qcoreapplication.h>
#include <QtGui/qtguiglobal.h>
#include <iostream>
#include "ui_MainWindow.h"
#include "opencv2/opencv.hpp"
#include "SerialPort.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = Q_NULLPTR);
private:
void initializationClicked();
void stopCapture();
void button1clicked();
void button2clicked();
signals:
void valueChangedButton1(unsigned int value);
private:
QPushButton *startCapture;
QPushButton *releaseCapture;
QPushButton *button1;
QPushButton *button2;
QLabel *interactionLabel;
QLabel *interactionType;
QFont f, i;
char *PORT;
bool b1Over, b2Over, isChecked;
// opencv stuff
cv::VideoCapture cap;
cv::Mat src;
// arduino stuff
char output[MAX_DATA_LENGTH];
char incomingData[MAX_DATA_LENGTH];
int intFromArduino;
char *port;
int fromArduino;
}
MainWindow.cpp
#include "MainWindow.h"
using namespace cv;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
f = QFont("Arial", 12, QFont::Bold);
i = QFont("Arial", 10, QFont::Black);
PORT = "\\\\.\\COM5";
width = 600;
height = 380;
setMinimumSize(width, height);
startCapture = new QPushButton("INIT SYSTEM", this);
startCapture->setGeometry(QRect(QPoint(10, height - 120), QSize(100, 50)));
connect(startCapture, SIGNAL(clicked()), this, SLOT(initializationClicked()));
releaseCapture = new QPushButton("KILL SYSTEM", this);
releaseCapture->setGeometry(QRect(QPoint(10, height - 60), QSize(100, 50)));
connect(releaseCapture, SIGNAL(clicked()), this, SLOT(stopCapture()));
button1 = new QPushButton("BUTTON 1", this);
button1->setGeometry(QRect(QPoint(width / 2 - 100, height - 120), QSize(150, 100)));
b1 = Point(width / 2 - 100, height - 120);
// receive the signal
connect(button1, SIGNAL(valueChangedButton1(unsigned int)), this, SLOT(button1clicked()));
button2 = new QPushButton("BUTTON 2", this);
button2->setGeometry(QRect(QPoint(width / 2 + 100, height - 120), QSize(150, 100)));
b2 = Point(width / 2 + 100, height - 120);
interactionLabel = new QLabel(this);
interactionLabel->setText("log: ");
interactionLabel->setGeometry(QRect(QPoint(10, 15), QSize(200, 20)));
interactionLabel->setFont(f);
interactionType = new QLabel(this);
interactionType->setText("kind of interaction");
interactionType->setGeometry(QRect(QPoint(110,15), QSize(250, 20)));
interactionType->setFont(i);
}
void MainWindow::initializationClicked()
{
controllMainVideoFlow();
}
void MainWindow::controllMainVideoFlow()
{
SerialPort arduino(PORT);
cap = VideoCapture(0);
if(!cap.isOpen())
{
cout << "no video feed" << endl;
}else
{
if(arduino.isConnected())
{
while(cap.isOpen())
{
cap >> src;
arduino.readSerialPort(output, MAX_DATA_LENGTH);
fromArduino = strlen(output);
if(fromArduino == 0)
{
isChecked = false;
}
else if(fromArduino != 0)
{
isChecked = true;
// emit the signal
emitSignalB1();
}
imshow("SOURCE", src);
qApp->processEvents();
if (cv::waitKey(30) == 'q') break;
}
cap.release();
src.release();
}
}
}
void MainWindow::emitSignalB1()
{
// emit the signal
emit valueChangedButton1(fromArduino);
}
void MainWindow::stopCapture()
{
interactionType->setText("system killed");
cap.release();
src.release();
destroyAllWindows();
}
void MainWindow::button1clicked()
{
interactionType->setText("button 1 clicked");
}
void MainWindow::button2clicked()
{
interactionType->setText("button 2 clicked");
}
The error is here:
connect(button1, SIGNAL(valueChangedButton1(unsigned int)), this, SLOT(button1clicked()));
Actually, there is no such signal called valueChangedButton1() in QPushButton. You get this error because button1 is a QPushButton.
You defined this signal in the MainWindow class, so you have to connect the signal from a MainWindow instance, not from a QPushButton instance.
You could write instead:
connect(this, SIGNAL(valueChangedButton1(unsigned int)), this, SLOT(button1clicked()));
// ^
// Not button1
This will fix your error.
But as it's not clear for me what you really want to do, I'm not sure if it is what you really want.
By the way, I think this is a typo but, in your header, you declared your signal as signal: instead of signals: (a 's' is missing).
EDIT: I would advise you to not use the old macros SIGNAL() and SLOT() anymore. You should use the new syntax instead.
Moreover, as the new syntax makes explicit from where the signals and slots are called, the origin of this error would have been obvious.
I have to check whether my process has finished and I need to convert it to bool because I want to you if.
In MainWindow.h I have created an object
QProcess *action;
In mainwindow.cpp
void MainWindow:: shutdown()
{
action=new QProcess(this);
action->start("shutdown -s -t 600");
//and now I want to use if
if (action has finished)
{
QMessageBox msgBox;
msgBox.setText("Your computer will shutdown in 1 minute.");
msgBox.exec();
}
You should connect to the process's finished signal. Your code will get invoked whenever the process finishes. E.g.
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-finished-msg-38232236
#include <QtWidgets>
class Window : public QWidget {
QVBoxLayout m_layout{this};
QPushButton m_button{tr("Sleep")};
QMessageBox m_box{QMessageBox::Information,
tr("Wakey-wakey"),
tr("A process is done sleeping."),
QMessageBox::Ok, this};
QProcess m_process;
public:
Window() {
m_layout.addWidget(&m_button);
m_process.setProgram("sleep");
m_process.setArguments({"5"});
connect(&m_button, &QPushButton::clicked, &m_process, [=]{ m_process.start(); });
connect(&m_process, (void(QProcess::*)(int))&QProcess::finished, [=]{ m_box.show(); });
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Window window;
window.show();
return app.exec();
}
I have a multiserverapp that works fine so far. I got 4 cpp files.
Main.cpp constructs the program. MainWindow.cpp constructs the ui and starts (via buttonclick) MyServer.cpp. MyServer.cpp creates a thread and starts MyThread.cpp.
My aim is to show several major steps (like the "server started", "new connection", etc..) on a textBrowser.
I pass the outputs from MyServer.cpp via emit updateUI("server started"); to mainwindow.cpp where the output gets catched by:
//Mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "myserver.h"
#include "mythread.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::AppendToBrowser(const QString text)
{
ui->textBrowser->append(text);
}
void MainWindow::on_startButton_clicked()
{
MyServer* mServer = new MyServer;
connect(mServer, SIGNAL(updateUI(const QString)), this, SLOT(AppendToBrowser(const QString)));
mServer->StartServer();
ui->textBrowser->setPlainText("Server Started");
}
That works just right because the connect command is just in the mainwindow.cpp itself.
The problem starts one step "deeper" in the mythread.cpp.
I created another signal in the
//MyThread.h
signals:
void updateUI_serv(const QString text);
and connected it in the MyServer.cpp with the MainWindow.cpp.
//MyServer.cpp
#include "myserver.h"
#include "mainwindow.h"
MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
void MyServer::StartServer()
{
if(!this->listen(QHostAddress::Any,1234))
{
qDebug("Server Error");
}
else
{
qDebug("Server started");
}
}
void MyServer::incomingConnection(int socketDescriptor)
{
qDebug("new connection");
MyThread *thread = new MyThread(socketDescriptor,this);
MainWindow *mMain = new MainWindow;
connect(thread, SIGNAL(updateUI_serv(const QString)),mMain ,SLOT(AppendToBrowser(const QString)));
//flags thread for selfdeletion
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
//calls run
thread->start();
emit updateUI("thread started!");
}
// MyThread.cpp
#include "mythread.h"
#include "mainwindow.h"
#include "myserver.h"
MyThread::MyThread(int ID, QObject *parent) :
QThread(parent)
{
this->socketDescriptor = ID;
emit updateUI_serv("start");
}
void MyThread::run()
{
//thread stars here
qDebug("Starting thread");
socket = new QTcpSocket();
emit updateUI_serv("hallo");
//set socketdescriptor number
if(!socket->setSocketDescriptor(this->socketDescriptor))
{
emit error(socket->error());
return;
}
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
qDebug("client connected");
exec();
}
void MyThread::readyRead()
{
QByteArray Data = socket->readAll();
QString Datain = QString::fromLatin1(Data);
qDebug("Date in:");
emit updateUI_serv("data in:");
socket->write(Data);
}
void MyThread::disconnected()
{
qDebug("Disconnected");
socket->deleteLater();
exit(0);
}
The connect command lays here "in between" the signal (updateUI_serv from mythread.cpp) and the slot (AppendToBrowser from mainwindow.cpp) file.
At this point the program crashes as soon as I try to write data (as a client via telnet) to the serverapp.
I tried to set the connect command into the mainwindow and the mythread as well, but both times I get different problems (like debugging problems, or the text does just not show up in the textBrowser).
Thanks so far.
Eventually the myServer-Object is not running in the MainThread, therefor accessing an ui element from another thread crashes your app.
You can make sure only Messages from mainThread will get displayed by adding the following code to your AppendToBrowser Slot:
if( QApplication::instance()->thread() == QThread::currentThread() )
ui->textBrowser->setPlainText("whateverTextThereShallBe");
else
return;
//You should not run into this else...
This if section checks if the current object calling the update is the mainThread. The else-section checks for erros. If you are running in the else-section you are trying to change ui-elements form a thread which is not the mainThread (UI-Thread). Connect your SIGNAL in server to another SIGNAL (SIGNAL->SIGNAL connection) and add a connect to SIGNAL(SERVER) -> SLOT(MainWindow) in your MainWindow.cpp. Eventually try your connect-call with the 5th. parameter for Queued Connection (Qt::QueuedConnection IIRC).
Ahh i got it on my own.
I solved the problem by creating a NEW function ( void forwarding(const Qstring); ) and in that function i emitted it with the ordinary emit updateUI(text); .. stuff works finaly!
I created a connection between server and client, the connection works fine in console, but i coudn't connect my QTcpServer class to GUI with signals and slots. Here is my code :
ServerTCP.cpp
ServerTcp :: ServerTcp (QWidget *parent)
{
listen(QHostAddress::Any,4000);
QObject:: connect(this, SIGNAL(newConnection()),
this, SLOT(connectionRequest()));
}
void ServerTcp :: connectionRequest()
{
emit IHM_connection();
clientConnection = nextPendingConnection();
QObject:: connect(clientConnection, SIGNAL(readyRead()),
this, SLOT(read()));
}
void ServerTcp::read()
{
QString ligne;
while(clientConnection->canReadLine())
{
ligne = clientConnection->readLine();
emit IHM_text(ligne);
}
QTextStream text(clientConnection);
}
ManinWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(&server,SIGNAL(IHM_connetion()),this,SLOT(connectionRequested()));
QObject::connect(&server,SIGNAL(read(QString)),this,SLOT(print_text(QString)));
}
void MainWindow::on_quitButton_clicked()
{
qApp->quit();
}
void MainWindow::print_text(QString text){
ui->log->append("message : " + text);
}
void MainWindow::connectionRequested(){
ui->log->append("connection OK!");
}
MainWindow::~MainWindow()
{
delete ui;
}
You have a typo in connect method: IHM_connetion
QObject::connect(&server,SIGNAL(**IHM_connetion**())
while the emitted signal is:
emit IHM_connection()
QObject:connect returns a bool value which indicates if signal-slot connection was successful. Checking this value (for example, with Q_ASSERT) is a good practice and can save you a lot of time in case of typos like this. .