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.
Related
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);
so i am writing a simple video player program and i did the same steps as the lesson i am taking but when i run the program and click on functionalities like end (which is close()) and open (open file) they dont work, i used the slot triggering as per the lesson although i saw different ways of using the menubar here but i must follow this format, here is my code:
header:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QVideoWidget>
#include <QStyle>
#include <QMediaPlayer>
#include <QFileDialog >
namespace Ui {
class videoWidget;
}
class videoWidget : public QMainWindow
{
Q_OBJECT
QMediaPlayer *meinPlayer;
QPushButton *playButton;
QPushButton *stopButton;
public:
explicit videoWidget(QWidget *parent = 0);
~videoWidget();
private slots:
void listeUndTitelAktualisieren();
void on_action_End_triggered();
void on_action_ffnen_triggered();
void on_action_Stop_triggered();
void on_action_PlayBack_triggered();
void on_action_Pause_triggered();
private:
Ui::videoWidget *ui;
};
#endif // MAINWINDOW_H
cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
videoWidget::videoWidget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::videoWidget)
{
ui->setupUi(this);
meinPlayer = new QMediaPlayer(this);
meinPlayer->setMedia(QUrl::fromLocalFile("/beispiele/topologien.wmv"));
meinPlayer->play();
}
void videoWidget::listeUndTitelAktualisieren()
{
QString titel = meinPlayer->media().canonicalUrl().toLocalFile();
ui->listWidget->addItem(titel);
this->setWindowTitle("Multimedia-Player – " + titel);
connect(meinPlayer, SIGNAL(mediaChanged(QMediaContent)), this, SLOT(listeUndTitelAktualisieren()));
}
void videoWidget::on_action_End_triggered()
{
this->close();
}
void videoWidget::on_action_ffnen_triggered()
{
QFileDialog *meinDialog = new QFileDialog(this);
meinDialog->setAcceptMode(QFileDialog::AcceptOpen);
meinDialog->setWindowTitle("Datei öffnen");
meinDialog->setNameFilters(QStringList() << "Videos (*.mp4 *.wmv)" << "Audios (*.mp3)" << "Alle Dateien (*.*)");
meinDialog->setDirectory(QDir::currentPath());
meinDialog->setFileMode(QFileDialog::ExistingFile);
if (meinDialog->exec() == QDialog::Accepted) {
QString datei = meinDialog->selectedFiles().first();
meinPlayer->setMedia(QUrl::fromLocalFile(datei));
/*QString fileName = QFileDialog::getOpenFileName(this,
tr("Open Image"), "/home/jana", tr("Video & Audio Files (*.mp3 *.mp4 *.wmv)"));
*/
meinPlayer->play();
}
}
void videoWidget::on_action_Stop_triggered()
{
meinPlayer->pause();
}
void videoWidget::on_action_PlayBack_triggered()
{
meinPlayer->play();
}
void videoWidget::on_action_Pause_triggered()
{
meinPlayer->pause();
}
am pretty sure this instruction here:
connect(meinPlayer, SIGNAL(mediaChanged(QMediaContent)), this, SLOT(listeUndTitelAktualisieren()));
is not located where it should coz you are connecting the signal toa slot INSIDE of the SLOT implementation....
try moving that to the constructor of dein videoWidget
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...
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;
}
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!