QErrorMessage keeps on appearing - c++

I count some entries and want to emit a message, when the user has many entries, since it will be confusing.
Nevertheless the other hand the user should have the option to disable this warning.
That's why I wanted to use a QErrorMessage.
But my QErrorMessage kept on appearing even when it should not (/when the checkbox is unchecked).
This is the shortest code I wrote:
void checkNumber(int const &n)
{
if(n > CriticalNumber)
{
QErrorMessage msg(this);
msg.showMessage("too much!");
}
}
Did I forget anything?
The funny thing is, after you once unchecked the checkbox, it is unchecked in every next call...
// edit:
This error happens even when the QErrorMessage is a member of my class and not initialised in every call.
// edit2:
By now I am pretty sure, that this error only occurs, when I use QString::arg. I did not use this in the example code, since I thought this would make no difference. So the example should look like this:
void showError(int const &n, QErrorMessage *msg)
{
msg->showMessage(tr("%1 is too big").arg(n));
}
showError() is called in the previous if-statement.

I solved this problem (specified in edit2).
The problem is, that the QErrorMessage saves all the QStringsthat should not be shown again.
Since my arg() creates nearly every time a new QStringthe QErrorMessageis shown each time it is changed.
Example:
QErrorMessage msg(this);
showError(1, msg);
showError(2, msg);
showError(1, msg);
The first showError(1, msg) will show the QErrorMessage.
If you uncheck the checkbox, showError(2, msg) will be shown (because a different QString is shown), but not showError(1, msg) (since the shown QString is the same as the first one.

I cannot reproduce your problem. What you should to is make checkNumber a member of a class, and do the same for your msg object.
Here is a working example:
mainwinodw.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QErrorMessage>
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void showErrorMsg();
private:
Ui::MainWindow *ui;
QErrorMessage msg;
QTimer timer;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&timer, SIGNAL(timeout()), this, SLOT(showErrorMsg()));
timer.start(3000); // we use a timer to show an error message
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showErrorMsg()
{
msg.showMessage("My message");
}

Related

How to get live variable updates on a statusbar in C++ with Qt's signals and slots?

I am using a proprietary driver API in my C++ / Qt application. There is an object named "status" which is essentially an integer value, that can be used to monitor errors. A value of 0 indicates all is fine, a negative value is a specific error. I would like to program a statusbar (using Qt's QStatusBar) which will give me live updates with the "status" current value as I execute parts of the code. A simplified example of what I currently have:
DriverInterfaceClass.h
#ifndef DRIVERINTERFACECLASS_H
#define DRIVERINTERFACECLASS_H
#include <QObject>
#include "multicam.h"
class DriverInterfaceClass : public QObject
{
Q_OBJECT
public:
DriverInterfaceClass(QObject *parent = nullptr);
~DriverInterfaceClass();
private:
void setStatus(int value);
private:
MCSTATUS status;
signals:
void statusChanged(const QString& status, int timeout);
};
#endif // DRIVERINTERFACECLASS_H
DriverInterfaceClass.cpp
#include "driverinterfaceclass.h"
DriverInterfaceClass::DriverInterfaceClass(QObject *parent)
: QObject(parent)
{
setStatus(McOpenDriver(NULL));
status = McSetParamStr(MC_CONFIGURATION, MC_ErrorLog, "error.log");
}
DriverInterfaceClass::~DriverInterfaceClass()
{
setStatus(McCloseDriver());
}
void DriverInterfaceClass::setStatus(int value)
{
status = value;
QString statusStr = QString::number(status);
emit statusChanged(statusStr, 0);
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "driverinterfaceclass.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
DriverInterfaceClass *driver;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
driver = new DriverInterfaceClass(this);
connect(driver, &DriverInterfaceClass::statusChanged, ui->statusbar, &QStatusBar::showMessage);
}
MainWindow::~MainWindow()
{
delete ui;
}
I have a problem figuring out how to update the statusbar with the value of the "status" variable. I am not getting any messages in my statusbar with this code. Perhaps I am missing something in the way I initialize my variable?
Cause
The statusChanged signal suffers from an unperceived existence. In other words, the driver object screams - STATUS CHAAAAAAANGED!!!!, but no one hears it. Here is why.
Your code is executed in the following order:
driver = new DriverInterfaceClass(this);
Then, as part of the constructor:
setStatus(McOpenDriver(NULL));
status = McSetParamStr(MC_CONFIGURATION, MC_ErrorLog, "error.log");
Only then is the connect statement executed:
connect(driver, &DriverInterfaceClass::statusChanged, ui->statusbar, &QStatusBar::showMessage);
Obviously, the signal is emited by setStatus BEFORE it is connected to the slot of ui->statusbar.
Note: You also set the value of status directly, without calling setStatus, i.e. status = McSetParamStr(..., thus the signal is not emited at all. However, calling setStatus instead would not help, because of the same reason as above.
Moving connect before new would cause a compiler error, because you cannot connect to something, which does not exist.
Solution
Do not emit signals from constructors. Do it when an event occurs.
To satisfy your invariant "emit signal whenever status is set" you need to use setStatus(McSetParamInt(... whatever)); method, don't modify status = xxx; directly (apart from the body of setStatus method). Then just change your setter function a bit to also emit the information about desired message to be displayed on the QStatusBar:
void DriverInterfaceClass::setStatus(int value)
{
if (status != value)
{
status = value;
emit statusChanged(myStatusValueToString(status));
}
}
where signal decl should be:
signals:
void statusChanged(const QString& msg);
and your myStatusValueToString just a simple mapping:
QString myStatusValueToString(int status)
{
if (status == 0) return "OK";
... etc ...
}
The connection should look like:
QObject::connect(driver, &DriverInterfaceClass::statusChanged, statusBar, &QStatusBar::showMessage);

Can't bind signal to slot in my Qt application

I'm new to Qt and I have a very simple demo app. It just include a QLineEdit widget and I want to invoke a function test() when I press ctrl+p in the QLineEdit.
Below are the related files.
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QShortcut>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QShortcut *s = new QShortcut(QKeySequence("Ctrl+P"), ui->lineEdit);
connect(s, SIGNAL(activated()), ui->lineEdit, SLOT(test()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void test(){
qDebug() << "test() triggered!" << endl;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void test();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
When I compile the application, I saw below messages in the debug panel and the application didn't respond to ctrl+p.
QObject::connect: No such slot QLineEdit::test() in ..\ShortcutIssueDemo\mainwindow.cpp:13
QObject::connect: (receiver name: 'lineEdit')
What's the problem with it?
You have 2 misconceptions:
The connection indicates the link between the object that emits the signal, the signal, the object to which the slot belongs and the slot. In your case it is obvious that the object to which the slot "slot" belongs is this.
If the old syntax (SIGNAL & SLOT) is to be used then "test" must be declared as slot.
So for the above there are 2 possible solutions:
Change to :
connect(s, SIGNAL(activated()), this, SLOT(test()));
public slots:
void test();
Or use new syntax:
connect(s, &QShortcut::activated, this, &MainWindow::test);
Between both solutions, the second one is better since it will indicate errors in compile-time instead of silent errors in run-time.
By default, the context of the shortcut is Qt::WindowShortcut, that is, it will fire when the key combination is pressed and the window has focus, if only when QLineEdit has focus then you have to change the context to Qt::WidgetShortcut:
s->setContext(Qt::WidgetShortcut);
You have received the error message saying there is no such slot...
Note that u haven't marked test() as slot, hence in <mainwindow.h>, replace
void test();
by
public slots: void test();
And the slot test() belongs to the mainwindow not to s, hence use this instead of s

Get clicked item from QListWidget and QTableWidget

I need to read an item from both QTableWidged and Q ListWidget as the user clicks on them.
For QListWidget I tried the solution described here, however the used SIGNAL itemClicked never seems to trigger.
For QTableWidget I tried multiple solutions, however they either didn't work or weren't what I need. Is there a simple solution for QTableWidget and am I just overlooking something with the solution provided for QListWidget?
Edit:
My Constructor of my MainWindow.cpp looks like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->TableWidget->verticalHeader()->setVisible(true);
QTableWidget* table = ui->TableWidget;
connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
this, SLOT(showBuchungsDetails(parseListWidgetBuchung(QListWidgetItem*))));
QHeaderView *header = qobject_cast<QTableView *>(table)->horizontalHeader();
connect(header, &QHeaderView::sectionClicked, [this](int logicalIndex){
QString text = ui->TableWidget->horizontalHeaderItem(logicalIndex)->text();
ui->lnBuchungsnummer->setText(text);
});
}
And here is my header file for MainWindow:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMessageBox>
#include <QFileDialog>
#include <QListWidget>
#include "TravelAgency.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_actionDatei_einlesen_triggered();
void on_actionProgramm_beenden_triggered();
void on_actionBuchungen_anzeigen_triggered();
Customer* parseListWidgetBuchung(QListWidgetItem* item);
Customer* parseTableWidgetBuchung(QString item);
void showBuchungsDetails(Customer* c);
private:
Ui::MainWindow *ui;
TravelAgency travelagency{};
bool inputReady = false;
QStringList m_TableHeader;
};
#endif // MAINWINDOW_H
Edit 2:
I am using Qt Creator 4.8.2
Do not use the SLOT/SIGNAL syntax for C++ signals and slots. This is error prone, since mistakes like this are not caught during compilation. Your code compiles fine but doesn't work.
Use Qt5 connect syntax. In this case, you can use a lambda:
connect(ui->listWidget, &QListWidget::itemClicked, this, [this](QListWidgetItem* item)
{
showBuchungsDetails(parseListWidgetBuchung(item));
});
The connect call is wrong. If you use the SIGNAL - SLOT syntax, the slot must be a single function (it's a "reference" to the function).
You can do something like this:
connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
this, SLOT(onItemClicked(QListWidgetItem*)));
And the onItemClicked implementation:
void MainWindow::onItemClicked(QListWidgetItem* item)
{
showBuchungsDetails(parseListWidgetBuchung(item));
}
Thanks for the suggestions, but I managed to sidestep the ´connect´ syntax completely by rightclicking on the widgeds in question on my UI window and use the "Go to slot..." functionality to create
void MainWindow::on_listWidget_itemClicked(QListWidgetItem *item)
{
parseListWidgetBuchung(item);
}
void MainWindow::on_TableWidget_cellClicked(int row, int column)
{
parseTableWidgetBuchung(ui->TableWidget->item(row, 0)->text());
}
which then allowed me to get the respective items. Either way, thanks again for the help!

Can't conect two frames with custom signals

I'm trying to connect two frames with a custom signal but I'm not really getting it.
This code is just an example of what im trying to do in my program, my objective is to transfer data between frames.
Files:
(sender)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void send();
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
private slots:
void on_pushButton_clicked();
};
#endif // MAINWINDOW_H
On "mainwindow.cpp" I've got the void on_pushButton_clicked() that emits the signal and shows the new frame:
private slot void:
void MainWindow::on_pushButton_clicked()
{
emit send();
Dialog sw;
sw.setModal(true);
sw.exec();
}
(receiver):
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog();
private slots:
void receive();
private:
Ui::Dialog *ui;
int a;
};
#endif // DIALOG_H
and the .cpp:
#include "dialog.h"
#include "ui_dialog.h"
#include "mainwindow.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
a=0;
MainWindow w;
connect(&w, SIGNAL(send()), this, SLOT(receive()));
qDebug() << a;
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::receive(){
qDebug() << "ola";
a++;
}
Conclusion:
So basicly the function Dialog doesn't print the qDebug(), and 'a' is still 0, so I conclude that the connection isn't set/executed.
Thanks all,
Best regards,
Dylan Lopes.
edit: Wrote a conclusion on the end of the post.
Consider the code in your Dialog constructor...
MainWindow w;
connect(&w, SIGNAL(send()), this, SLOT(receive()));
This creates a locally scoped MainWindow on the stack and connects its send() signal to the Dialog's receive() slot. But the MainWindow -- and, hence, the connection -- will be destroyed as soon as the Dialog constructor has completed.
In addition, looking at MainWindow::on_pushButton_clicked...
void MainWindow::on_pushButton_clicked()
{
emit send();
Dialog sw;
sw.setModal(true);
sw.exec();
}
You emit the send() signal before constructing the Dialog.
I don't really know enough about what you're trying to achieve to provide a definitive answer, but in the interests of getting some kind of signal/slot interaction you might want to do the following: change the Dialog constructor to...
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
ui->setupUi(this);
a=0;
qDebug() << a;
}
And change MainWindow::on_pushButton_clicked to...
void MainWindow::on_pushButton_clicked()
{
Dialog sw;
connect(this, &MainWindow::send, &sw, &Dialog::receive);
emit send();
sw.setModal(true);
sw.exec();
}
That should at least result in Dialog::receive being invoked and you can work from there.
Connection between a signal and a slot doesn't mean that the signal function will be triggered.
You still need to emit your signal so that a gets updated.
Creating an empty slot isn't working either, as slots are the receiving point of a signal. In this case, on_pushButton_clicked() gets triggered whent he push button is clicked. This doesn't trigger send unless you call EMIT(send()) (IIRC, you emit a signal with EMIT, is that still the case?).

Qt - connecting a signal/slot in a triggered action

I'm using Qt 5 on a Windows and building a GUI App with multiple QDialog classes. I am trying to connect a signal from a QDialog in a triggered action of the QMainWindow class after instances of both have been created. I have read the documentation on Qt here: http://doc.qt.io/qt-4.8/signalsandslots.html and here: https://wiki.qt.io/New_Signal_Slot_Syntax. I have also read through many questions on stackoverflow that have helped correct some of the initial errors I was getting, but haven't helped me solve this problem.
The error I keep getting is:
"expected primary-expression before ',' token"
I have tried both the old syntax for connect
connect(sender, SIGNAL (valueChanged(QString,QString)),
receiver, SLOT (updateValue(QString)) );
and the new syntax (which is shown in the .cpp file below)
connect(sender, &Sender::valueChanged,
receiver, &Receiver::updateValue );
The MainWindow is created in the main.cpp and the 2nd dialog is created on_action_someAction_triggered(), so I know that the instances I am referencing exist. Is there a better way for me to connect the SIGNAL and the SLOT?
Here is the code I am working with (minus the extra unrelated code).
mainwindow .h:
#include <QMainWindow>
#include "shipdia.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void loadSelectedShip(QString shipName);
private slots:
void on_actionNew_Ship_triggered();
private:
Ui::MainWindow *ui;
shipdia *sDialog;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTextStream>
#include <QObject>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionNew_Ship_triggered()
{
sDialog = new shipdia(this);
QObject::connect(&shipdia, //this is were I attempt to
&shipdia::sendShip, //connect the signal/slot
this,&MainWindow::loadSelectedShip); //but always get an error
sDialog ->show();
}
void MainWindow::loadSelectedShip(QString shipName)
{
... do something ... //this code works, but the signal is never received
}
qdialog.h
#ifndef SHIPDIA_H
#define SHIPDIA_H
#include "functions.h"
#include <QDialog>
namespace Ui {
class shipdia;
}
class shipdia : public QDialog
{
Q_OBJECT
public:
explicit shipdia(QWidget *parent = 0);
~shipdia();
private slots:
void on_pushButton_2_clicked();
signals:
void sendShip(QString shipName);
private:
Ui::shipdia *ui;
};
#endif // SHIPDIA_H
qdialog.cpp
#include "shipdia.h"
#include "ui_shipdia.h"
#include <QObject>
#include <QMessageBox>
#include <QTextStream>
#include <QDir>
shipdia::shipdia(QWidget *parent) :
QDialog(parent),
ui(new Ui::shipdia)
{
ui->setupUi(this);
}
shipdia::~shipdia()
{
delete ui;
}
void shipdia::sendSelectedShip(QString shipName)
{
emit sendShip(shipName); //I previously just emitted sendSelectedShip,
//but separating the function did not fix it.
}
void shipdia::on_pushButton_2_clicked()
{
//Code below functions up to next comment
QString shipName = ui->line_23->text();
shipName = QDir::currentPath() + "/shipFolder/" + shipName + ".txt";
QFile shipFile(shipName);
QStringList stringList;
if (shipFile.open(QIODevice::ReadOnly))
{
QTextStream in(&shipFile);
while(!in.atEnd())
{
QString line = in.readLine();
if(line.isNull())
break;
else
stringList.append(line);
}
shipFile.close();
}
//Code above functions ^
sendSelectedShip(shipName); //this line does not produce an error
}
I think, the code should be
sDialog = new shipdia(this);
QObject::connect(sDialog,
&shipdia::sendShip,this,&MainWindow::loadSelectedShip);
and it should be placed in the constructor of the MainWindow, right after ui->setupUi(this); and the on_actionNew_Ship_triggered() function should look like this:
void MainWindow::on_actionNew_Ship_triggered()
{
sDialog ->show();
}
In your original code, a new instance of shipdia will be created everytime the on_actionNew_Ship_triggered() is called. That should be avoided.
Hope this helps.