QString username = ui->lineEdit->text();
QString password = ui->lineEdit_2->text();
QMessageBox Failed;
Failed.setWindowFlags(Qt::FramelessWindowHint);
if(username == "Jon" && password == "12345")
{
Failed.setText("Login failed. Try again.");
Failed.exec();
} else {
Failed.setText(password);
Failed.exec();
}
Using qt.
Sorry if this has been asked before I'm quite new and couldn't find a answer.
I don't understand what I'm doing wrong. I set username and password to the text inside the line edits on the ui. But everytime i click this button function the output of the dialog text is always blank. How can i get it so the the text is read?
alright, I ran some tests and this is what i found got:
if you set up some sort of signal slot connection that calls a function containg those if/else statmentsthe program will work. this is the following program i made:
dialog.h:
#ifndef DIALOG_H
#define DIALOG_H
#include "ui_dialog.h"
#include <QDialog>
class DialogClass : public QDialog, public Ui::Dialog
{
Q_OBJECT
public:
DialogClass(QWidget *parent = 0);
public slots:
void check();
};
#endif // DIALOG_H
Dialog.cpp:
#include "dialog.h"
#include <QMessageBox>
DialogClass::DialogClass(QWidget *parent) : QDialog(parent)
{
setupUi(this);
connect(pushButton, SIGNAL(clicked()), this, SLOT(check()));
}
void DialogClass::check()
{
QString username = lineEdit->text();
QString password = lineEdit_2->text();
QMessageBox Failed;
Failed.setWindowFlags(Qt::FramelessWindowHint);
if(username == "Jon" && password == "12345")
{
Failed.setText("Login failed. Try again.");
Failed.exec();
} else {
Failed.setText(password);
Failed.exec();
}
}
and main.cpp
#include <QApplication>
#include <QMessageBox>
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DialogClass w;
w.show();
return a.exec();
}
bassically, all that I do is make a dialog class a lot like your main function, only that mine has a button dialog.ui has a button. After that, I use the button to establish a signal slot connection that ensures that when this button is clicked, it will call the check() function (which is basically your if/else algorithm make into a slot), which checks to see if the username is "Jon", and password is "12345".
Why your original implementation didn't work?
I am sorry, but i am not sure.
However, this approach should work. I will add to the answer if I ever do figure out why the if statements weren't picking up. However, this approach should get your application working.
happy coding!
Related
I have a QProgressDialog and I would like to override its cancel() slot to change its behavior.
Instead of closing the dialog, I would like to do some other operations and then close the dialog after a QThread to finish before closing the dialog.
I tried to disconnect the canceled/cancel signal/slot couples and the reconnect with the new behavior but it does not seem to change much.
As as soon as I click on the cancel button, the progress dialog gets closed first and then my lambda get executed anyway.
Qobject::disconnect(m_progressdialog, &QProgressDialog::canceled, m_progressdialog, &QProgressDialog::cancel);
Qobject::connect(m_progressdialog, &QProgressDialog::canceled, [](){
// continue displaying the dialog as an occupation bar
m_progressdialog->setValue(0);
// do some other things
// a lot of code
// ...
// only later close the dialog
m_progressdialog->close();
});
Is there a way to do this correctly?
I don't know your whole code, but according to the documentation, the idea is more or less the same you're saying: a slot to connect the signal QProgressDialog::canceled().
The following code is just an example but it's working. In this case, instead of using the own Qt property wasCanceled, it is used a boolean to control when to stop and cancel the QProgressDialog.
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
class QProgressDialog;
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
~Dialog();
private slots:
void on_pushButton_clicked();
void my_custom_cancel();
private:
Ui::Dialog *ui;
QProgressDialog *progress;
int numTasks = 100000;
bool canceled = false;
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QProgressDialog>
#include <QThread>
#include <QDebug>
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
progress = new QProgressDialog("Task in progress...", "Cancel", 0, numTasks);
connect(progress, SIGNAL(canceled()), this, SLOT(my_custom_cancel()));
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_clicked()
{
progress->open();
for (int i = 0; i < numTasks; i++) {
progress->setValue(i);
QThread::usleep(100);
if (canceled)
break;
}
progress->setValue(numTasks);
}
void Dialog::my_custom_cancel()
{
qDebug() << "do something";
canceled = true;
}
I created a default Qt GUI app, I added void keyPressEvent(QKeyEvent* ev); in the mainwindow class, when user presses the space, the app will plays the sound (ok) but when the user press many times in a short duration, the app will not respond. I don't know why? Help me, please!
.pro file:
QT += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = untitled2
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
FORMS += mainwindow.ui
RESOURCES += \
res.qrc
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMediaPlayer>
namespace Ui { class MainWindow; }
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void keyPressEvent(QKeyEvent* ev);
private:
Ui::MainWindow *ui;
QMediaPlayer mp;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{ ui->setupUi(this); }
MainWindow::~MainWindow() { delete ui; }
void MainWindow::keyPressEvent(QKeyEvent* ev) {
switch(ev->key()) {
case Qt::Key_Space: {
mp.setMedia(QUrl("qrc:/sounds/Fireworks.wav"));
mp.play();
break;
}
}
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
(Note: I did upload mainwindow.ui. res.qrc here)
mp.setMedia(QUrl("qrc:/sounds/Fireworks.wav"));
Do not set the media unnecessarily, as the mediaplayer doesnt check internally "Oh the media is the same I am going to be clever"
Rather you want to start the media from the beginning when its key is pressed again. The simplest solution is to have a boolean which indicate if the media is set.
case Qt::Key_Space:
{
if(!is_media_set)
{
mp.setMedia(QUrl("qrc:/sounds/Fireworks.wav"));
is_media_set = true;
}
mp.setPosition(0);
mp.play();
break;
}
If several keys trigger different sounds replace the boolean with current_media_key to indicate the last media loaded.
To setMedia, the documentation says:
Setting this property to a null QMediaContent will cause the player to
discard all information relating to the current media source and to
cease all I/O operations related to that media.
Each time one presses space again, the media file has to be loaded from disk, it might be a good idea to load it once in the constructor or an initialization method, to save the loading time (access to a hard disk is always slow)
To run the sound again, add also setPosition(0) before calling play().
I'm not sure this is the best answer, but this is my final result:
void MainWindow::keyPressEvent(QKeyEvent* ev) {
switch(ev->key()) {
case Qt::Key_Space: {
qDebug() << mp.mediaStatus() << mp.state();
if(mp.state() == QMediaPlayer::StoppedState) {
mp.setMedia(QUrl("qrc:/sounds/Fireworks.wav"));
}
mp.setPosition(0);
mp.play();
break;
}
}
}
I have QDialogButtonBox buttons on my dialog which are Ok and Cancel button pair. I have implemented accepted() signal to process when 'Ok' button is pressed but I want to abort quitting dialog if the directory path is invalid.
void SettingsDialog::on_buttonBox_accepted()
{
QDir path( ui->lineEditRootPath->text() );
if ( path.exists( ui->lineEditRootPath->text() ) )
{
QSettings settings; // save settings to registry
settings.setValue(ROOT_PATH, ui->lineEditRootPath->text() );
}
else
{
// abort cancelling the dialog here
}
}
Can the dialog quitting be abort from this handler? Do I have to implement the above code in some other signal? Do I have to use simple button to accomplish this instead of QDialogButtonBox?
This issue comes from the dialog template bundled with Qt Creator. When you create an empty dialog with buttons, the .ui file has connections between the button box and and the underlying dialog. They are created behind your back, so to speak:
So, there really is no problem, since the button box doesn't actually accept the dialog. You must accept the dialog, if you don't then the dialog stays open.
The simple fix is to remove the default connection(s).
Other Nitpicks
You should not use the QDir::exists(const QString &) overload - it won't work. You already provided the path to dir's constructor. Simply use exists().
Thus:
void SettingsDialog::on_buttonBox_accepted()
{
QDir path(ui->lineEditRootPath->text());
if (!path.exists()) return;
QSettings settings; // save settings to registry
settings.setValue(ROOT_PATH, ui->lineEditRootPath->text());
accept(); // accepts the dialog, closing it
}
You could also use the static QFileInfo::exists:
void SettingsDialog::on_buttonBox_accepted()
{
if (! QFileInfo.exists(ui->lineEditRootPath->text()) return;
...
}
Finally, it's probably a nice idea to provide some sort of feedback to the user when an input is invalid. In C++11, that's quite easy to do:
#include <QApplication>
#include <QFileInfo>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QGridLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog dialog;
QLineEdit edit("/");
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
QGridLayout layout(&dialog);
layout.addWidget(&edit, 0, 0);
layout.addWidget(&buttons, 1, 0);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [&]{
if (!QFileInfo::exists(edit.text())) return;
//...
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, [&]{ dialog.reject(); });
QObject::connect(&edit, &QLineEdit::textChanged, [&](const QString&){
if (QFileInfo::exists(edit.text()))
edit.setStyleSheet("");
else
edit.setStyleSheet("* { background: red; }");
});
dialog.show();
return a.exec();
}
After some testing, you've realized that the users have a bad tendency to enter paths that might be on disconnected network volumes. When you attempt to check if they exist, it blocks the GUI just so that the OS can politely tell you "umm, nope".
The solution is to perform the check in a worker thread, so that if it blocks, the UI will not be directly affected. If the worker thread blocks, the path editor background will turn yellow. If the path doesn't exist, the background will turn red and the OK button will be disabled.
One bit of code requires some explanation: QObject::connect(&checker, &Checker::exists, &app, [&](...){...}) connects the checker's signal to a lambda in the thread context of the application object. Since checker's signals are emitted in the checker's thread, without the context (&app), the code would be executed in the checker's thread. We definitely don't want that, the GUI changes must be executed in the main thread. The simplest way to do it is to pass one object we surely know lives in the main thread: the application instance. If you don't pass the proper context, e.g. QObject::connect(&checker, &Checker::exists, [&](...){...})), you'll get undefined behavior and a crash.
#include <QApplication>
#include <QFileInfo>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QThread>
#include <QTimer>
class Thread : public QThread {
using QThread::run; // final
public:
~Thread() { quit(); wait(); }
};
class Checker : public QObject {
Q_OBJECT
public:
Q_SIGNAL void exists(bool, const QString & path);
Q_SLOT void check(const QString & path) { emit exists(QFileInfo::exists(path), path); }
};
int main(int argc, char *argv[])
{
bool pathExists = true;
QApplication app(argc, argv);
QDialog dialog;
QLineEdit edit("/");
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
QGridLayout layout(&dialog);
layout.addWidget(&edit, 0, 0);
layout.addWidget(&buttons, 1, 0);
QTimer checkTimer;
Checker checker;
Thread checkerThread;
checker.moveToThread(&checkerThread);
checkerThread.start();
checkTimer.setInterval(500);
checkTimer.setSingleShot(true);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [&]{
if (!pathExists) return;
//...
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, [&]{ dialog.reject(); });
QObject::connect(&edit, &QLineEdit::textChanged, &checker, &Checker::check);
QObject::connect(&edit, &QLineEdit::textChanged, &checkTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
QObject::connect(&checkTimer, &QTimer::timeout, [&]{ edit.setStyleSheet("background: yellow"); });
QObject::connect(&checker, &Checker::exists, &app, [&](bool ok, const QString & path){
if (path != edit.text()) return; // stale result
checkTimer.stop();
edit.setStyleSheet(ok ? "" : "background: red");
buttons.button(QDialogButtonBox::Ok)->setEnabled(ok);
pathExists = ok;
});
dialog.show();
return app.exec();
}
#include "main.moc"
I have the MainWindow class. In the constructor of this class I want to start a new thread that will do some work. But I get this error:
Assert failure in QWidget: "Widgets must be created in the GUI thread."
In this new thread I am not creating any widgets. This is what I have tried so far. Could someone help me on solving this problem? In don't have experience with signals and slots and I will really appreciate some advises.
newThread.h
#ifndef NEWTHREAD_H
#define NEWTHREAD_H
#include <QThread>
#include "mainwindow.h"
class NewThread : public QThread
{
Q_OBJECT
public:
explicit NewThread(QObject *parent = 0);
signals:
public slots:
protected:
void run();
};
#endif // NEWTHREAD_H
newThread.cpp
#include "newthread.h"
NewThread::NewThread(QObject *parent) :
QThread(parent) { }
void NewThread::run(){
MainWindow m;
m.updateInBackground();
}
MainWindow.cpp
MainWindow::MainWindow(QStringList applications, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ReadFromRegistry read;
this->setFixedSize(435,280);
ui->setupUi(this);
appsNames = applications;
this->apps = read.getApplicationsFromRegistry(appsNames);
ui->updateInBackgroundCkb->setChecked(false);
//read from settings.xml the time interval
QString time = RWXml::readSettingsFile();
if(time.compare("-1") != 0){
NewThread th;
while(true){
th.start();
th.sleep(time.toLong(0,10));
}
}
}
EDIT:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStringList apps;
QString app = "AppTest1";
apps.append(app);
app = "AppTest2";
apps.append(app);
app = "AppTest3";
apps.append(app);
MainWindow w(apps);
w.create();
w.show();
return a.exec();
}
I instantiate the MainWindow in main. But i need to access the method from MainWindow in the run method of the NewThread. That's why it is instantiated in the NewThread.
EDIT:
void MainWindow::updateInBackground(){
ClientSocket client;
for(Application ap : getApps()){
QString currentVersion = ap.getAppVersion();
QString appCode = ap.getAppCode();
QString appSerial = ap.getAppSerialNo();
client.connect();
QString message = "2//" + currentVersion + "//" + appCode + "//"+ appSerial;
//send message to the server
client.sendMessage(message);
//receiver message from the server
QString received = client.receiveMessage();
//check if the current version is the last one
if(received.compare("0") != 0){
//if is not the last one, set the new version
ap.setAppVersion(received);
//set the update date
ap.setCurrentDate();
//write in windows registry
WriteInRegistry::writeRegistry(ap);
//update the xml file containg the updates of this application
updateXMLFile(ap);
}
}
//read from registry
ReadFromRegistry read;
//populate the grid from the MainWindow with the new data
populateTable(read.getApplicationsFromRegistry(getAppsNames()));
client.closeConnection();
}
Your issue with your code is that you create the mainwindow and the qt application in different threads. The main window seems to be created in your "new thread", whereas the qt application is not.
You also seem to have a circular dependency between the mainwindow constructor and the run method of the thread.
You would need to move the mainwindow creation into your main.cpp which is also a logical place for it.
That being said, please do take a look at the url below and all the references in the post for getting some further thoughts.
How to Use QThread in the Right Way (Part 1)
How to Use QThread in the Right Way (Part 2)
I am working with Qt creator to make a GUI program that takes in different URLs and will download and display the html code.
The user can add different URLs to a listWidget. Then the user can select a specific URL and download the html which will be displayed beside the list of URLs.
The problem I am having is getting the text area to update after the reply is received.
main.cpp - Basically just shows the window.
#include <QtGui/QApplication>
#include "mainwindow.h"
#include "htmlmanager.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h - Pretty straight forward. Contains the object html that will be used to request the html from the inputted website.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "htmlmanager.h"
#include <QString>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
HtmlManager html;
private slots:
void on_addButton_clicked();
void on_actionExit_triggered();
void on_removeButton_clicked();
void on_downloadButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp - This is the beginning of the problem. If you look down at the "downloadButton_clicked()" function, you see that it fetches the html by sending a request. However, the reply isn't recieved before the next line so the text field is set to "".
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_addButton_clicked()
{
if(!ui->lineEdit->text().isEmpty())
{
ui->listWidget->addItem(ui->lineEdit->text());
}
else
{
qDebug() << "Input field is empty.";
}
}
void MainWindow::on_actionExit_triggered()
{
//doesn't do anything right now
}
void MainWindow::on_removeButton_clicked()
{
if(ui->listWidget->currentItem()) //If an item is selected
{
delete ui->listWidget->currentItem();
}
else
{
qDebug() << "No selection";
}
}
void MainWindow::on_downloadButton_clicked()
{
if(ui->listWidget->currentItem()) //If an item is selected
{
html.fetch(ui->listWidget->currentItem()->text());
ui->textBrowser->setText(html.str);
}
else
{
qDebug() << "No selection";
}
}
htmlmaneger.h
#ifndef HTMLMANAGER_H
#define HTMLMANAGER_H
#include <QObject>
#include <QDebug>
#include <QtNetwork>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QString>
class HtmlManager : public QObject
{
Q_OBJECT
public:
HtmlManager();
void fetch(QString Url);
QString str;
public slots:
void replyFinished(QNetworkReply* pReply);
private:
QNetworkAccessManager* m_manager;
};
#endif // HTMLMANAGER_H
htmlmanager.cpp - Once the reply is received, it stores the html in the QString "str"
#include "htmlmanager.h"
HtmlManager::HtmlManager()
{
m_manager = new QNetworkAccessManager(this);
connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
}
void HtmlManager::fetch(QString Url)
{
m_manager->get(QNetworkRequest(QUrl(Url)));
qDebug() << "Sending network request.";
}
void HtmlManager::replyFinished(QNetworkReply* pReply)
{
qDebug() << "Recieved network reply.";
QByteArray data=pReply->readAll();
str = data;
}
Is there an easy way to send the value of str to the MainWindow class once the reply is received, or is there a way for the onclick function to wait to update the text area until after a reply is received?
You definitely don't want to wait for a reply in your onClick() function. That will cause your program to be unresponsive until the network request comes it (which could very well take "forever").
One way to attack this would be to add a signal to your HtmlManager class. Something maybe called stringReceived. Then, in your mainwindow class you'd just need to add a line like this:
connect(html, SIGNAL(stringReceived(QString)), ui->textBrowser, SLOT(setText(QString));
QNetworkAccessManager don't provide synchronous or blocking approach. If you want to wait until reply has been received you have to go for waiting in a local event loop until reply finished signal invoked.
See the following link for turning asynchronous operation into synchronous one:
http://doc.qt.digia.com/qq/qq27-responsive-guis.html
In the section "Waiting in a Local Event Loop" they have shown an example using QNetworkAccessManager. You can use the same approach with timeout and local event loop.