for a project I need to integrate a lot of data in a QListView, I get the data from a QThread and store it in a QMap<QString, QString>, until then I have no problem of crash or freeze. But when in my QThread I browse my QMap to call for each element the function that will add the QString in the QListView, then I have a freeze. I searched a lot but I didn't find or understand how to do it. I add you a part of the simplified code to show you my problem :
exemple.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QListView>
#include <QStandardItemModel>
#include <QDebug>
#include <QThread>
#include "mythread.h"
class exemple : public QMainWindow
{
Q_OBJECT
public:
explicit exemple(QWidget *parent = nullptr);
private:
QPushButton *btn_start;
QListView *lv_file;
QMap<QString, QString> myMap;
MyThread *m_thread;
QStandardItemModel *model;
public slots:
void addFileInformation(QString, QString);
void startShow();
private slots:
void startThread();
};
#endif // MAINWINDOW_H
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QDebug>
#include <QDirIterator>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent);
void run();
signals:
void addFileInformation(QString, QString);
void startShow();
};
#endif // MYTHREAD_H
exemple.cpp
#include "exemple.h"
exemple::exemple(QWidget *parent) : QMainWindow(parent)
{
setFixedSize(750,750);
btn_start = new QPushButton("start", this);
btn_start->setGeometry(50, 40, 150, 30);
btn_start->setCursor(Qt::PointingHandCursor);
lv_file = new QListView(this);
lv_file->setGeometry(50, 100, 600, 500);
m_thread = new MyThread(this);
model = new QStandardItemModel(this);
lv_file->setModel(model);
connect(btn_start, SIGNAL(clicked()), this, SLOT(startThread()));
connect(m_thread, SIGNAL(addFileInformation(QString, QString)),this, SLOT(addFileInformation(QString, QString)));
connect(m_thread, SIGNAL(startShow()),this, SLOT(startShow()));
}
void exemple::startThread(){
m_thread->start();
}
void exemple::addFileInformation(QString param1, QString param2)
{
myMap.insert(param1, param2);
}
void exemple::startShow(){
for (int i = 0; i < myMap.size(); i++){
model->appendRow(new QStandardItem(myMap.keys()[i]));
}
}
In this example it would be when the thread calls the startShow slot that my freeze appears.
I understand that I'm not in the thread anymore and that's the reason of the freeze but I have the same problem if instead of adding information to a QMap on "exemple" as I do here, the addFileInformation slot of example.cpp displayed directly the information. Like this:
exemple.cpp
void exemple::addFileInformation(QString param1, QString param2)
{
model->appendRow(new QStandardItem(param1));
}
If you have a solution, a track or a link to do this without freezing I'm interested :)
If I'm not clear don't hesitate to ask me to explain it again, thanks to those who will take their time to help me.
If your dataset is large, you probably need to implement your own model that uses the data in the fashion you're storing it. Models in Qt are tricky, but once you've done a few they begin to make sense.
I've got an example on my github: https://github.com/jplflyer/qt-TreeViewDemo. It's probably not perfect. I wrote it while trying to figure out how to write it. This is for QTreeView, but QListView won't be that different.
I also have a generic version here: https://github.com/jplflyer/Git-Dashboard. Look down into the tree for GenericItemModel. It's a template and is kind of cool, actually. It also may not be fully featured, but it might help.
You can try to unset model from view lv_file->setModel(nullptr); to get rid of unnecessary view refreshes (after each row insert), change model and set it back to view.
You must use a signal to send your mapdata to the ui thread and append a row in your thread, not just append a row directly in your thread.
i have review u code carefully,and maybe u should try this way:
void exemple::startShow()
{
QList<QStandardItem*> itemList;
for (int i = 0; i < myMap.size(); i++)
{ itemList<<(new QStandardItem(myMap.keys()[i]));
}
model->appendRow(itemList);
}
Related
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!
I want to search files by name in a particular location as selected by the user. I want that as soon as I got the file. It must be put in QTreeWidget parallely and showing a QMovie(":/images/img_searching.gif") while searching is in progress until user did not stop searching.
ThreadSearch.h
#ifndef QTHREADSEARCH_H
#define QTHREADSEARCH_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QFileInfoList>
class QThreadSearchFileName : public QThread
{
Q_OBJECT
public:
QThreadSearchFileName(QObject *parent = 0);
~QThreadSearchFileName();
void run();
void getAllfiles(QStringList, QDir);
signals:
void fileInfoList(QFileInfo);
private:
QMutex m_Mutex;
QWaitCondition m_WaitCondition;
};
#endif
ThreadSearch.cpp
#include "ThreadSearch.h"
#include <QApplication>
#include <QThread>
QThreadSearchFileName::QThreadSearchFileName(QObject *parent):QThread(parent)
{
}
QThreadSearchFileName::~QThreadSearchFileName()
{
m_WaitCondition.wakeOne();
wait();
}
void QThreadSearchFileName::run()
{
QMutexLocker locker(&m_Mutex);
}
void QThreadSearchFileName::getAllfiles(QStringList targetStrList, QDir currentdir)
{
for(long int i1=0; i1<targetStrList.size(); i1++)
{
QString targetStr;
targetStr = targetStrList[i1];
QDirIterator it(currentdir, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString filename = it.next();
QFileInfo file(filename);
if (file.isDir())
{ // Check if it's a dir
continue;
}
if (file.fileName().contains(targetStr, Qt::CaseInsensitive))
{
emit fileInfoList(file);
}
}
}
}
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include <QDirIterator>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThreadSearchFileName *m_pSearchFileNameThread = new QThreadSearchFileName;
for(int i=0; i<userSelectedpathList.size(); i++)
{
QDir dir(userSelectedpathList[i]);
m_pSearchFileNameThread ->getAllfiles(stringListToBeSearch, dir);
connect(m_pSearchFileNameThread,SIGNAL(fileInfoList(QFileInfo)),this,SLOT(searchFileNameResult(QFileInfo)));
}
return a.exec();
}
void main::searchFileNameResult(QFileInfo file1) //Now Making SearchFile Name Tree
{
QTreeWidgetItem *SearchTreeItem = new QTreeWidgetItem(m_psearchProgresswdgt->finalSearchList_treeWidget);
SearchTreeItem->setCheckState(0,Qt::Unchecked);
SearchTreeItem->setText(1,file1.baseName());
}
It is good practice to separate such operations out from GUI object. What is more I would suggest higher-level async mechanism provided by QObject:
Make some class which could handle searching, for example
SearchingClass:
class SearchingClass : public QObject {
Q_OBJECT
public:
void setSomeSearchParametersOrSomething(QObject* something);
public slots:
void search();
signals:
void found(QObject* objectThatHasBeenFound);
}
Create instance of this class and move it into another thread:
auto searchingObject = new SearchingClass();
searchingObject->setSomeSearchParametersOrSomething(...);
auto thread = new QThread();
searchingObject->moveToThread(thread);
connect(this, SIGNAL(startSearchingSignal()), searchingObject, SLOT(search()));
connect(searchingObject, SIGNAL(found(QObject*)), this, SLOT(someHandleFoundSlot(QObject*)));
emit startSearchingSignal();
Make sure that found signal is being emitted every time that searching algorithm finds some result.
Ofc you must implement someHandleFoundSlot and declarate startSearchingSignal signal in GUI class.
I assume that you barely know Qt framework, so you should read about signals and slots as well as Qt meta-object system to fully understand whole code.
EDIT:
I see that you have edited your question. Your problem has several solution, I will describe you, what you did wrong comparably to what I had posted here.
Do not extend QThread. Extend QObject instead. It makes you can call moveToThread method. Crete an instance of QThread and pass it to this method. It causes later execution of slots to be performed in the thread you passed.
Do not make identical connections in loop until you want it to be executed more than once.
Make method getAllfiles (or search in my example) to be slot and do not call it manually. When you call method manually, it will always be performed in the same thread. Just connect it to some signal, and emit that signal.
[Just like you emit signal when you find matching file – the result is being handled in the slots objects thread.]
It's your decision, if you want to have thread for every single userSelectedpathList element. I would advice you to do it in one working thread (it's disc operations, I think it wouldn't be faster) and iterate that list inside getAllfiles method.
Previously I gave you generic answer about "how to do async work in Qt". Here's a simple implementation for your use case (I hope you will learn something).
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileInfo>
#include <QDirIterator>
#include <QThread>
#include <QDebug>
#include <QPushButton>
#include "filesearchingclass.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0){
setCentralWidget(&pushButton);
connect (&pushButton, SIGNAL(clicked(bool)), this, SLOT(main())); //invoke this->main() in event loop when button is pressed
qRegisterMetaType<QFileInfo>("QFileInfo");
searchingThread.start(); //thread must be started
}
~MainWindow(){
searchingThread.quit();
searchingThread.wait();
}
public slots:
void main() {
//EXAMPLE PARAMETERS:
QStringList pathList;
pathList.append("/home");
pathList.append("/var/log");
QStringList stringListToBeSearch;
stringListToBeSearch.append(".jpg");
stringListToBeSearch.append(".log");
//-------------------
auto fileSearchingObject = new FileSearchingClass(); //dynamic as you can't destroy object when it is out of scope
fileSearchingObject->moveToThread(&searchingThread); //important!!!
fileSearchingObject->setTargetStrList(stringListToBeSearch);
fileSearchingObject->setPaths(pathList);
connect(this,SIGNAL(startSearching()),fileSearchingObject,SLOT(search())); //do not call fileSearchingObject->search() manually
connect(fileSearchingObject,SIGNAL(foundFile(QFileInfo)),this,SLOT(searchFileNameResult(QFileInfo))); //handle every result in event loop
connect(fileSearchingObject, SIGNAL(searchFinished()), fileSearchingObject, SLOT(deleteLater())); //no need to wory about deleting fileSearchingObject now
emit startSearching(); //like calling fileSearchingObject->search() but in another thread (because of connection)
}
signals:
void startSearching();
public slots:
void searchFileNameResult(QFileInfo fileInfo) {
//do something
qDebug() << "---FOUND---" << fileInfo.absoluteFilePath() << "\n";
}
private:
QThread searchingThread;
QPushButton pushButton;
};
#endif // MAINWINDOW_H
filesearchingclass.h
#ifndef FILESEARCHINGCLASS_H
#define FILESEARCHINGCLASS_H
#include <QFileInfo>
#include <QDirIterator>
#include <QThread>
#include <QDebug>
class FileSearchingClass : public QObject {
Q_OBJECT
public:
~FileSearchingClass(){}
void setPaths (const QStringList &paths) {
userSelectedPathList = paths;
}
void setTargetStrList(const QStringList &value) {
targetStrList = value;
}
public slots:
void search() {
for(int i=0; i<userSelectedPathList.size(); i++) {
QDir currentDir(userSelectedPathList[i]);
for(long int i1=0; i1<targetStrList.size(); i1++)
{
QString targetStr;
targetStr = targetStrList[i1];
QDirIterator it(currentDir, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString filename = it.next();
QFileInfo file(filename);
if (file.isDir())
{ // Check if it's a dir
continue;
}
if (file.fileName().contains(targetStr, Qt::CaseInsensitive))
{
emit foundFile(file); //calling MainWindow::searchFileNameResult directly is possible, but bad idea. This thread is only focused in searching, not modifing widgets in GUI.
}
}
}
}
emit searchFinished(); //it's always good to know when job is done.
}
signals:
void foundFile(QFileInfo);
void searchFinished();
private:
QStringList userSelectedPathList;
QStringList targetStrList;
};
#endif // FILESEARCHINGCLASS_H
I am trying to make a ruler in Qt. I am developing an application which requires a line edit in which the user can select any part of the string. When the selection is done, the start and end co-ordinates of the selection are passed to the backend.
The ruler which is shown with the line edit has a precision of one character or blank space in the string.
I have built a similar widget using Java and ExtJS some time ago. I am trying to simulate the same with Qt for quite some time now but not succeeding in doing so.
Please take a look at the image to understand what it looks like. I want to know whether it is possible or not in Qt. If it is possible what widget should I use to achieve it?
I am not 100% sure I understand your question correctly.
You can write a class inheriting from QLineEdit to emit signals when its selection has changed.
Below is a simple example of such a class: it takes the native signal selectionChanged and has two new signals which emit information about the changed text just to show what could be possible. I also include a simple dialog class to show how to use this (even if it's just a crude way).
lineedit.h
#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H
#include <QLineEdit>
class LineEdit: public QLineEdit
{
Q_OBJECT
public:
LineEdit(QWidget *parent = 0): QLineEdit(parent)
{
connect(this, SIGNAL(selectionChanged()), this, SLOT(textSelectionChanged() ));
}
public slots:
void textSelectionChanged() {
emit selectionChanged(this->selectedText());
emit selectionChanged(this->selectionStart(), this->selectedText().length() );
}
signals:
void selectionChanged(const QString&);
void selectionChanged(int , int );
};
#endif
dialog.h
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include <QDialog>
#include <QtCore>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0) : QDialog(parent) {}
public slots:
void textChanged(const QString &string) {
qDebug() << string;
}
void textChanged(int start, int length) {
qDebug() << "Start"<< start << ", length: " << length;
}
};
#endif
main.cc (for completeness)
#include <QApplication>
#include "dialog.h"
#include "lineedit.h"
#include <QHBoxLayout>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Dialog dialog;
QHBoxLayout *layout = new QHBoxLayout(&dialog);
LineEdit lineedit;
layout->addWidget(&lineedit);
QObject::connect(&lineedit,SIGNAL(selectionChanged(QString)), &dialog, SLOT(textChanged(QString)));
QObject::connect(&lineedit,SIGNAL(selectionChanged(int,int)), &dialog, SLOT(textChanged(int,int)));
dialog.show();
return app.exec();
}
Let me know if this helps.
I am trying to learn Qt and I am attempting to do so by making a little titres game. Currently I have a 2d array which represents the game board.
Every second this 2d array is changed by a thread (representing the passage of time) and then this thread emits a signal telling the main GUI to update based on the new game board.
My Thread is as follows:
gamethread.h
#ifndef GAMETHREAD_H
#define GAMETHREAD_H
#include <QtCore>
#include <QThread>
#include<QMetaType>
class GameThread : public QThread
{
Q_OBJECT
public:
explicit GameThread(QObject *parent = 0);
void run();
private:
int board[20][10]; //[width][height]
void reset();
signals:
void TimeStep(int board[20][10]);
};
#endif // GAMETHREAD_H
gamethread.cpp
#include "gamethread.h"
#include <QtCore>
#include <QtDebug>
//Game Managment
GameThread::GameThread(QObject *parent) :
QThread(parent)
{
reset();
}
void GameThread::reset()
{
...
}
//Running The Game
void GameThread::run()
{
//Do Some Stuff
emit TimeStep(board);
}
and the main UI which should receive the signal and update based on the new board is:
tetris.h
#ifndef TETRIS_H
#define TETRIS_H
#include <QMainWindow>
#include "gamethread.h"
namespace Ui{
class Tetris;
}
class Tetris : public QMainWindow
{
Q_OBJECT
public:
explicit Tetris(QWidget *parent = 0);
~Tetris();
GameThread *mainThread;
private:
Ui::Tetris *ui;
private slots:
int on_action_Quit_activated();
void on_action_NewGame_triggered();
public slots:
void onTimeStep(int board[20][10]);
};
#endif // TETRIS_H
tetris.cpp
#include <QMessageBox>
#include <QtGui>
#include <boost/lexical_cast.hpp>
#include <string>
#include "tetris.h"
#include "ui_tetris.h"
Tetris::Tetris(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Tetris)
{
ui->setupUi(this);
mainThread = new GameThread(this);
connect(mainThread, SIGNAL(TimeStep(int[20][10])),
this, SLOT(onTimeStep(int[20][10])),
Qt::QueuedConnection);
}
Tetris::~Tetris()
{
delete ui;
}
void Tetris::onTimeStep(int board[20][10])
{
//receive new board update my display
}
void Tetris::on_action_NewGame_triggered()
{
mainThread->start();
}
When I run this I get:
QObject::connect: Cannot queue arguments of type 'int[20][10]'
(Make sure 'int[20][10]' is registered using qRegisterMetaType().)
I have looked into qRegisterMetaType and Q_DECLARE_METATYPE but I am not even remotely sure how to use them or even if I must use them. Can someone give the QT newbie some assistance?
You can wrap the board data in a class. It won't work if you merely typedef'd it, since Qt will try to use non-array operator new to create instances of board data. The compiler will detect it and rightfully complain.
It's bad style to derive from QThread like you are doing and use it as a generic QObject. QThread is conceptually a thread controller, not a thread itself. See this answer for the idiomatic way to do it. Your GameThread should be a QObject, not a QThread.
So:
struct Board {
int data[20][10];
}
Q_DECLARE_METATYPE(Board);
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
qRegisterMetatype<Board>("Board");
...
Game * game = new Game;
QThread thread;
game->connect(&thread, SIGNAL(started()), SLOT(start());
game->connect(game, SIGNAL(finished()), SLOT(deleteLater()));
thread.connect(&game, SIGNAL(finished()), SLOT(quit());
game.moveToThread(&thread);
thread.start(); // you can start the thread later of course
return app.exec();
}
class Game: public QObject
{
QTimer timer;
Board board;
public slots:
void start() {
connect(&timer, SIGNAL(timeout()), SLOT(tick()));
timer.start(1000); // fire every second
}
void finish() {
timer.stop();
emit finished();
}
protected slots:
void tick() {
... // do some computations that may take a while
emit newBoard(board);
// Note: it probably doesn't apply to trivial computations in
// a Tetris game, but if the computations take long and variable
// time, it's better to emit the board at the beginning of tick().
// That way the new board signal is always synchronized to the timer.
}
signals:
void newBoard(const Board &);
void finished();
}
What would happen if later you decided to change the size of the board? I think it would be better to encapsulate the concept of a board in an object, and pass around a pointer to said object.
I am trying to program an App that fetches files from a server.
I have a 'Window' class(mainwindow.cpp, which is a widget class that would be the UI) and then I have a 'Backend' class(Backend.cpp).
The GUI has a push button and two radio buttons. If the radio button "remote" is seleted, then upon clicking the push button will lead to fetching files from server.
However, there is some problem in the 'connect' call in Backend.cpp which I can't figure out. The error I get is: no matching function call to 'QObject::connect(QNetworkReply*&), const char[13], Backend* const, const char[20])'
Here are the codes:
ANSWER: Avoid circular inclusions!!!!
Here are the updated codes:
mainwindow.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include <QRadioButton>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>
#include <QFile>
#include <QUrl>
#include "Backend.h"
class QGroupBox;
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
QTcpSocket *conn;
QFile *file;
QUrl url;
Backend backend_inst;
private:
QRadioButton *button_local;
QRadioButton *button_remote;
QGroupBox *createPushButtonGroup();
private slots:
void onClick_button1();
void onCheck_local();
void onCheck_remote();
};
#endif
mainwindow.c
#include <QtGui>
#include "mainwindow.h"
Window::Window(QWidget *parent)
: QWidget(parent)
{
QGridLayout *grid = new QGridLayout;
grid->addWidget(createPushButtonGroup(), 1, 1);
setLayout(grid);
setWindowTitle(tr("File-Fetch App"));
resize(480, 420);
}
QGroupBox *Window::createPushButtonGroup()
{
QGroupBox *groupBox = new QGroupBox();
QPushButton *pushButton1 = new QPushButton(tr("Fetch Files!!"));
button_local = new QRadioButton(tr("&Download Files from Local Storage"));
button_remote = new QRadioButton(tr("&Download Files from a Web-Server"));
button_local->setChecked(1);
connect(pushButton1,SIGNAL(clicked()), this, SLOT(onClick_button1()));
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(pushButton1);
vbox->addSpacing(50);
vbox->addWidget(button_local);
vbox->addWidget(button_remote);
vbox->addStretch(1);
groupBox->setLayout(vbox);
return groupBox;
}
void Window::onClick_button1()
{
QTextStream out(stdout);
out << "fetch button clicked\n";
if (button_local->isChecked()){
onCheck_local();
}
else if (button_remote->isChecked()){
onCheck_remote();
}
}
void Window::onCheck_local()
{
QTextStream out(stdout);
out << "local update button checked\n";
}
void Window::onCheck_remote()
{
QTextStream out(stdout);
out << "remote update button checked\n";
QString pathname= "http://192.168.1.1:8000/example.txt";
QUrl webaddr = pathname;
backend_inst.FetchFile(webaddr);
}
Backend.h
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QTextStream>
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=0);
void FetchFile(QUrl fpath);
public slots:
void getBytesFromFile();
private:
QNetworkReply *reply;
QNetworkAccessManager qnam;
};
#endif // BACKEND_H
Backend.cpp
#include "Backend.h"
Backend::Backend(QObject* parent)
: QObject(parent)
{
}
void Backend::FetchFile(QUrl fpath)
{
reply = qnam.get(QNetworkRequest(fpath));
QObject::connect(reply, SIGNAL(readyRead()),this, SLOT(getBytesFromFile()));
//qnam = new QNetworkAccessManager;
//QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile()));
}
void Backend::getBytesFromFile(){
QByteArray downloadedData;
QTextStream out(stdout);
out << "we are loading data from URL\n";
downloadedData =reply->readAll();
out << downloadedData;
delete reply;
}
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
To use signals and slots, your classes (both signaling and slotting) must derive from QObject, i.e.
#include "mainwindow.h"
#include <QObject>
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=0);
[...]
Backend::Backend(QObject* parent)
: QObject(parent)
{
}
You posted this:
class Backend
{
// Q_OBJECT
public:
Backend();
void FetchFile(QUrl fpath);
public slots:
void getBytesFromFile();
private:
QNetworkReply *reply;
QNetworkAccessManager qnam;
};
Q_OBJECT is still commented if yes remove it.. you are using signal and slots..
EDIT :
try to avoid circular inclusion:
you included Backend in mainwindow and viceversa..
The commented out lines:
qnam = new QNetworkAccessManager;
QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile()));
Were what's causing your issue. Connect excepts a pointer, not a pointer-to-pointer. qnam is a pointer was a pointer in the previous version of the code and using the address-of operator on it would turn it into a pointer-to-pointer. Second mistake is that you need to have the same signature for your signal and slot in order to get it called (otherwise you get a runtime error). So, correctly:
connect(qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile(QNetworkReply*)));
(and obviously, change the actual signature of the getBytesFromFile method).
As to why your error persists despite commenting the code out: I think you are running an old build and the new one is failing to build due to the vtable issue (as you described in the comment thread). My guess is that qmake is glitching out, which I have experienced when adding the Q_OBJECT macro to already existing classes. Go to your build folders and delete Makefile* everywhere. Then go back to Qt creator and rebuild the project, that should force qmake to generate the Makefiles again.
It seems you have some problems with build mechanism. With posted code and un-commented
QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile())); in Backend::FetchFile function, all your code works. Running and with checked "Download Files from Web-Server" it prints the "we are loading data from URL" from getBytesFromFile - isn't this the slot you want to be called ?