Qt PushButton + clicked() not always executing - c++

After hours of debugging and searching for an answer on the web, I am a little bit in despair. This is my first small Qt project and what I try to do is the following:
I want to build a little calculation game. It looks like this:
Application before first click
After clicking on "Check / New" there should be a change to the following: Application after first click
But often I do have to click on that button multiple times, to get it triggered. The "Quit" button works perfectly fine, so it does not seem to be a general problem, but probably shitty coding from my side.
Here is my code, I hope you have got some hints:
(1) mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
int a, b, result;
private slots:
void CmdCheckNewClicked();
};
#endif // MAINWINDOW_H
(2) mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <ctime>
void MainWindow::CmdCheckNewClicked() {
if(result != -1) {
int eingabe = ui->EdtInput->text().toInt();
QString comment;
if(eingabe==result) {
comment = "Right";
} else {
comment = "Wrong";
}
comment += QString(": %1 + %2 = %3").arg(a).arg(b).arg(result);
ui->LblComment->setText(comment);
ui->EdtInput->setText("");
}
a = rand() % 20 + 20;
b = rand() % 20 + 20;
result = a + b;
QString task = QString("%1 + %2").arg(a).arg(b);
ui->LblTask->setText(task);
ui->EdtInput->setFocus();
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
srand((unsigned)time(NULL));
connect(ui->CmdCheckNew, SIGNAL(clicked()),
SLOT(CmdCheckNewClicked()));
connect(ui->CmdQuit, SIGNAL(clicked()), SLOT(close()));
}
MainWindow::~MainWindow() {
delete ui;
}
Also in the further course of use of that application, during submitting of results, that PushButton does not execute properly everytime. I really have no more ideas for further debugging.

Sometimes, when you change objects "created" directly in Ui the functions does not work properly. To check if that the case, place a qDebug("here"); in the beggining of your SLOT function. If the debug is triggered, the connection is not the problem, then the fastest solution would be create your Ui element in the MainWindow object.

Related

How do I get the text from a QTableView cell into a QlineEdit on click Qt5 C++

So I have been trying all day to find a working example of how to retrieve the data from a QtableView into a lineEdit as a QString. I think I have tried every example code online and have had zero success, I can not even figure out how to pull the row and column numbers from the tableView. Everything I have tried fails and I get the error that index is a unused parameter. This has got to be something simple that I am missing but I have not used QT or done any C++ programing since version 3 and am completely baffled. mainWindow.cpp is below. Thanks in advance for any help you can give me. BTW everything works fine except for the clicked slot.
#include <QDebug>
#include <QAbstractTableModel>
#include <QModelIndex>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->setupUi(this);
// Create a data model for the mapping table from a CSV file
csvModel = new QStandardItemModel(this);
csvModel->setColumnCount(2);
//csvModel->setHorizontalHeaderLabels(QStringList() << "Name" << "Number");
ui->tableView->setModel(csvModel);
// Open the file
QFile file("/home/foo.csv");
if ( !file.open(QFile::ReadOnly | QFile::Text) ) {
qDebug() << "File not exists";
} else {
// Create a thread to retrieve data from a file
QTextStream in(&file);
//Read to end
while (!in.atEnd())
{
QString line = in.readLine();
QList<QStandardItem *> standardItemsList;
for (QString item : line.split(",")) {
standardItemsList.append(new QStandardItem(item));
}
csvModel->insertRow(csvModel->rowCount(), standardItemsList);
}
file.close();
}
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_tableView_clicked(const QModelIndex &index)
{
qDebug() << "test";
}
Header code below
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_tableView_clicked(const QModelIndex &index);
private:
Ui::MainWindow *ui;
QStandardItemModel *csvModel;
};
#endif // MAINWINDOW_H
I think this is just a basic misunderstanding with regard to how signals/slots operate. Unless you're using the auto connect feature you need to manually connect a signal to a slot using one of the QObject::connect overloads. So in your constructor you need something like...
connect(ui->tableView, &QTableView::clicked, this, &MainWindow::[on_tableView_clicked);

QDialog::move() not considering taskbar on Ubuntu with multiple screens

Normally, moving a QDialog using QDialog::move() positions the dialog outside of taskbars.
However, on Ubuntu 20.04 with two monitors it is not the case with frameless Dialogs :
This does not happen if the dialog is not frameless :
This behaviour has been observed on Ubuntu 20.04. It also happens only under some configurations :
Main monitor needs to be on the right side, with task bar on the left (between the two monitors)
Left monitor needs to have a lower resolution than the right one
Fractional scaling needs to be disabled
Here is the code for a minimally reproducible example used in the screenshots:
#ifndef BUGDIALOG_H
#define BUGDIALOG_H
#include <QDialog>
namespace Ui {
class BugDialog;
}
class BugDialog : public QDialog
{
Q_OBJECT
public:
explicit BugDialog(QWidget *parent = nullptr);
~BugDialog();
private slots:
void on_moveButton_clicked();
private:
Ui::BugDialog *ui;
};
#endif // BUGDIALOG_H
#include "bugdialog.h"
#include "ui_bugdialog.h"
BugDialog::BugDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::BugDialog)
{
ui->setupUi(this);
ui->xPosEdit->setText("3200");
ui->yPosEdit->setText("1000");
}
BugDialog::~BugDialog()
{
delete ui;
}
void BugDialog::on_moveButton_clicked()
{
int x = ui->xPosEdit->text().toInt();
int y = ui->yPosEdit->text().toInt();
if (x > -1 && x > -1)
move(x, y);
}
The main window is less interesting, it only creates the child window controlling its WindowFlags property :
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "bugdialog.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_framelessBox_stateChanged(int arg1);
private:
void hideDialog();
Ui::MainWindow *ui;
BugDialog* dialog;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
dialog = new BugDialog(nullptr);
dialog->hide();
}
MainWindow::~MainWindow()
{
delete ui;
delete dialog;
}
void MainWindow::on_pushButton_clicked()
{
if (dialog->isHidden())
{
dialog->show();
ui->pushButton->setText("Hide dialog");
}
else
{
hideDialog();
}
}
void MainWindow::on_framelessBox_stateChanged(int)
{
auto windowType = ui->framelessBox->isChecked() ? Qt::FramelessWindowHint : Qt::Dialog;
dialog->setWindowFlags(windowType);
hideDialog();
}
void MainWindow::hideDialog()
{
dialog->hide();
ui->pushButton->setText("Show dialog");
}
This looks like a bug in Qt. Does anyone know if it is expected behaviour? Or how to get around this?
I didn't find a proper solution or satisfying workaround for this issue, but found a partial solution that is half satisfying :
Before each move() on the dialog, set its flag to Qt::Window (no frameless) and hide it.
Override the moveEvent() handler, set the window flag to Qt::FramelessWindowHint and show it.
Here are the two changes I made on this example :
void BugDialog::on_moveButton_clicked()
{
int x = ui->xPosEdit->text().toInt();
int y = ui->yPosEdit->text().toInt();
if (x > -1 && x > -1)
{
hide();
setWindowFlags(Qt::Window);
move(x, y);
}
}
void BugDialog::moveEvent(QMoveEvent *)
{
QTimer::singleShot(500, this, [this](){
this->setWindowFlags(Qt::FramelessWindowHint);
this->show();
});
}
I also tried changing the dialog painting. The idea was to set window flags as a "framefull" dialog but paint the dialog as if it had the FramelessWindowHint flag. I found no acceptable/affordable solution with this idea.

How to identify the pressed button in Qt C++?

I have 4 buttons on my main window. Each button opens its own window with its own data. How to identify the pressed button to open right window? For example: I press sales button and it opens a window that shows information about ticket sales.
Mainwindow ui
Here is my code from mainwindow h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <sales.h>
#include <theatres.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void button_pressed();
private:
Ui::MainWindow *ui;
sales *s;
theatres *t;
};
#endif // MAINWINDOW_H
And here is my code from mainwindow cpp:
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "build/sqlite/sqlite3.h"
#include <QtSql/QSqlDatabase>
#include <QTableView>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect((*ui).pushButton,SIGNAL(released()), this, SLOT(button_pressed()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::button_pressed()
{
s = new sales(this);
s -> show();
}
As Andy Newman already answered
the shortest solution is a lambda function
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QHBoxLayout *h_layout = new QHBoxLayout;
centralWidget()->setLayout(h_layout);
for(int c =1; c <= 10; c++)
{
QPushButton *button = new QPushButton(this); // create button
button->setText(QString::number(c)); // set button id
h_layout->addWidget(button); // add a button to the form
// lambda magic
/* connecting a button signal to a lambda function that captures a pointer to a
button and invokes an arbitrary type function. */
connect(button, &QPushButton::clicked, [this, button]() {
pressedButton(button->text());
});
}
}
void MainWindow::pressedButton(const QString &id_button)
{
qDebug("Pressed button: %ls", id_button.utf16());
}
MainWindow::~MainWindow()
{
delete ui;
}
#include "widget.h"
#include "./ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->btn_0,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_1,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_2,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_3,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::SlotButtonClicked()
{
auto sender = this->sender();
if ( sender == ui->btn_0 ) {
// Click btn_0 to open widget0
} else if ( sender == ui->btn_1 ) {
// Click btn_1 to open widget1
} else if ( sender == ui->btn_2 ) {
// Click btn_2 to open widget2
} else if ( sender == ui->btn_3 ) {
// Click btn_3 to open widget3
}
}
If you can use Qt Designer, the best way to do this is to click with button right on the QPushButton (On .ui file in Qt Designer) and click to "Go to Slot", this will create a private slot to this button! In the header file will create the definition, like this:
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
And in the source file (.cpp) will create the "function" clicked pushButton:
void MainWindow::on_pushButton_clicked()
{
}
void MainWindow::on_pushButton_2_clicked()
{
}
void MainWindow::on_pushButton_3_clicked()
{
}
void MainWindow::on_pushButton_4_clicked()
{
}
Inside of the "function" in .cpp, you put the task that you want this button to do, in this case, to open a new window!
When you click "go to slot" in another button, will create another private slot with the respective number (If is the second QPushButton that you create, the private slot will be called by pushButton_2).
The usual way to do this would be to connect the 4 different buttons to 4 different slots. It looks like you are using QtDesigner so that shouldn't be an issue.
If you were generating an array of buttons at run time you'd run into problems and would need a different solution. You could try something like this to pass an array index to the function, for example:
connect(button[x], &QPushButton::clicked, this, [this, x]() { button_pressed(x); });
Or you could do it the Qt way, which would be to call ::setProperty to store data in the button, and then retrieve it from the event, but it's so esoteric that I can't actually remember how to do that...

How to access this variable Qt?

Sorry the question is going to be long. I dont know how to shorten this. I am new to Qt. With a little bit of experience in c++, I thought I should start with Qt to make my programs graphical. I chose a single user library management software as a personal project.
I made a book class to get and set the name, author and uid of the book. I then made a library class to manage a vector of books. It can add presently only add a book and get a book at a particular index.
So, the code so far works fine. But I then tried to add GUI.
In the mainwindow constructor, i just added two predefined books to the library class object. The main window has 3 Line-edits to show the name, author, and uid of each book. It has two buttons "next" to show the next Book and "previous" to show the previous book.
I wanted a feature to add the book. So, created File menu and added Add Book using the Design menu. I went to slot.
What i want is to create a second window to ask for the name, author and uid of the new book. The fact is my library class object contains the details of all the books. How do i access that object so as to call addBook() function to add the book.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "library.h"
#include "dialog.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
library getLib();
private slots:
void on_next_clicked();
void on_previous_clicked();
void on_actionAdd_book_triggered();
private:
Ui::MainWindow *ui;
int currentIndex;
library l;
Dialog* d;
};
#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);
book b;
b.setAuthor("Ruskin Bond");
b.setName("The Jungle Book");
b.setUid("123456789");
l.addBook(b);
b.setAuthor("Savi Sharma");
b.setName("This is not your story");
b.setUid("789456123");
l.addBook(b);
b = l.getBook(0);
ui->lineEdit->setText(QString::fromStdString(b.getName()));
ui->lineEdit_2->setText(QString::fromStdString (b.getAuthor()) );
ui->lineEdit_3->setText(QString::fromStdString(b.getUid()));
currentIndex = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_next_clicked()
{
++currentIndex;
if(currentIndex < l.numOfBooks())
{
book b;
b = l.getBook(currentIndex);
ui->lineEdit->setText(QString::fromStdString(b.getName()));
ui->lineEdit_2->setText(QString::fromStdString(b.getAuthor()));
ui->lineEdit_3->setText(QString::fromStdString(b.getUid()));
}
}
void MainWindow::on_previous_clicked()
{
--currentIndex;
if(currentIndex >= 0)
{
book b;
b = l.getBook(currentIndex);
ui->lineEdit->setText(QString::fromStdString(b.getName()));
ui->lineEdit_2->setText(QString::fromStdString(b.getAuthor()));
ui->lineEdit_3->setText(QString::fromStdString(b.getUid()));
}
}
void MainWindow::on_actionAdd_book_triggered()
{
d = new Dialog(this);
d->show();
}
Dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QMessageBox>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_clicked()
{
QString temp = ui->lineEdit->text(),
temp_2 = ui->lineEdit_2->text(),
temp_3 = ui->lineEdit_3->text();
if(temp == "" || temp_2 == "" || temp_3 == "")
QMessageBox :: warning(this, "Warning!", "One of the lines is empty");
else
{
book b(temp.toStdString(), temp_2.toStdString(), temp_3.toStdString());
//how do i add the book?
}
}
This is what that appears:
Your answer
I hope the addBook in library class is to add the book into the list. You can create one connection which will pass the details of the book as follow from Dialog to Mainwindow:-
In Dialog class create one signal which will send the details of the book. For example in Dialog.h in class Dialog declare signal like this:-
signals:
void bookDetailsEntered(book b);
In Dialog.cpp emit this signal in on_pushButton_clicked():-
void Dialog::on_pushButton_clicked()
{
QString temp = ui->lineEdit->text(),
temp_2 = ui->lineEdit_2->text(),
temp_3 = ui->lineEdit_3->text();
if(temp == "" || temp_2 == "" || temp_3 == "")
QMessageBox :: warning(this, "Warning!", "One of the lines is
empty");
else
{
book b(temp.toStdString(), temp_2.toStdString(), temp_3.toStdString());
emit bookDetailsEntered(b);
}
}
Now in MainWindow.h declare one slot which will receive the details of the book like this:-
private slots:
void onBookDetailsEntered(book b);
and in MainWindow.cpp create connection from the signal in Dialog.h to the slot in Mainwindow like this:-
void MainWindow::on_actionAdd_book_triggered()
{
d = new Dialog(this);
connect(d,SIGNAL(bookDetailsEntered(book)),
this,SLOT(onBookDetailsEntered(book)));
// FYI, You can use Dialog here like Dialog d(in stack instead of heap).
// By this the d variable will get destroyed once the d is out of scope.
// Here you're creating the multiple instance of Dialog(each time when you show Dialog which will consume more memory)
}
void MainWindow::onBookDetailsEntered(book b)
{
library.addBook(b);
}
I hope i answered your question.
you need to show the dialog in your on_actionAdd_book_triggered(). i.e.
d->show();

Trigger different event on QTimer reset

I am building a little program that shows traffic light images using QTimer. So i setup my timer and everything works good. But I cant figure out, how can I get the Robot Lights to ->show() and ->hide() each time the timer interval is reached. I can have this all wrong, im still learning so please advise.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void timer();
QTimer *mytimer;
private:
Ui::MainWindow *ui;
int timerValue;
private slots:
void showGreen();
void showYellow();
void showRed();
void on_startButton_clicked();
};
#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);
ui->green->setVisible(false);
ui->yellow->setVisible(false);
ui->red->setVisible(false);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timer(){
ui->green->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showYellow()));
mytimer->start(timerValue = (ui->spinBox->value())*1000);
}
void MainWindow::showYellow(){
ui->yellow->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showRed()));
mytimer->start(timerValue);
}
void MainWindow::showRed(){
ui->red->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showGreen));
mytimer->start(timerValue);
}
void MainWindow::showGreen(){
ui->green->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showYellow()));
mytimer->start(timerValue);
}
void MainWindow::on_startButton_clicked()
{
timer();
ui->startButton->setEnabled(false);
}
I Also disable the Start button when clicked so the timer cannot run twice.
So On the main Window UI I basicly Have a Spinbox that takes the input of the user which is the time that I pass that to Qtimer, and then I have the 3 images with lights red green yellow that has to show and hide in the intervals . So I created almost something like a manual loop. Qtimer starts and shows Green, then goes to ShowYellow Slot and then showRed slot, so now, its suppose to go to the Green slot and then to yellow, but it doesnt go to Green again.
Can someone tell me why not.
Read the comments:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
...
mytimer = new QTimer(this); // create QTimer once
}
void MainWindow::timer() {
timerValue = ui->spinBox->value() * 1000;
showGreen(); // reuse showGreen()
}
void MainWindow::showYellow() {
// this is how toggling can be done
ui->yellow->setVisible(!ui->yellow->isVisible());
mytimer->disconnect(); // disconnect before making a new connection
connect(mytimer ,SIGNAL(timeout()), this, SLOT(showRed()));
mytimer->start(timerValue);
}
void MainWindow::showRed() {
ui->red->setVisible(!ui->red->isVisible());
mytimer->disconnect();
connect(mytimer ,SIGNAL(timeout()), this, SLOT(showGreen()));
mytimer->start(timerValue);
}
void MainWindow::showGreen() {
ui->green->setVisible(!ui->green->isVisible());
mytimer->disconnect();
connect(mytimer ,SIGNAL(timeout()), this, SLOT(showYellow()));
mytimer->start(timerValue);
}
Perhaps most simply:
void MainWindow::showHide(){
ui->green->setVisible(!ui->green->isVisible());
}
The show() and hide() member functions of QWidget are equivalent to setVisible(true) and setVisible(false), respectively.
A problem you may run into later, if you press the start button more than once, is that you spawn a new interval timer every time, leading to quicker blinking with every press of the button. If this is not what you want to happen, you'll have to control the generation of new timers.