This question already has answers here:
Undefined reference to vtable. Trying to compile a Qt project
(21 answers)
Undefined reference to vtable, Qt in Linux
(5 answers)
undefined reference to vtable when compiling a Qt hello World
(2 answers)
Closed 4 months ago.
I am working in a C++ project with configured CMake. I added a test project to the solution.
Everything compiles and run ok with MSVC 2019 but with mingw 11.2 I get numerous errors:
undefined reference to `LoginCommObj::~LoginCommObj()'
undefined reference to `BasisCommObj::~BasisCommObj()
undefined reference to `BasisCommObj::~BasisCommObj()
undefined reference to `vtable for LoginCommObj
.......
Here is the minimum reproducible example:
Project structure:
---multiround
|
------viewmodels
|
------------cancelroundviewmodel.h
------communicationobjects
|
-----------cancelroundcommobj.h
-----------cancelroundcommobj.cpp
-----------basiscommobj.h
-----------basiscommobj.cpp
-------communicationtools.h
-------communicationtools.cpp
-------gameinfo.h
-------globaldata.h
-------globalgamedata.h
-------globaluserdata.h
-------multiplayerround.cpp
-------multiplayerround.h
-------CMakeLists.txt
---tests
|
------main.cpp
------cancelroundcommobjtest.h
------cancelroundcommobjtest.cpp
------CMakeLists.txt
The source code is as follows:
multiround/CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
project (libMultiRound)
cmake_policy(SET CMP0020 NEW)
cmake_policy(SET CMP0043 NEW)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_definitions(-DMAKE_MULTIPLAYERROUND_LIB)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${Qt6Widgets_INCLUDE_DIRS}
${Qt6Network_INCLUDE_DIRS}
${Qt6Core_INCLUDE_DIRS})
set(MULTIROUND_HEADR
communicationobjects/basiscommobj.h
globaldata.h
globalgamedata.h
globaluserdata.h
viewmodels/cancelroundviewmodel.h
communicationobjects/basiscommobj.h
communicationobjects/cancelroundcommobj.h
communicationtools.h
multiplayerround.h
gameinfo.h
)
set(MULTIROUND_SRCS
communicationobjects/basiscommobj.cpp
communicationobjects/cancelroundcommobj.cpp
communicationobjects/basiscommobj.cpp
communicationtools.cpp
multiplayerround.cpp
)
add_library(libMultiRound SHARED
${MULTIROUND_SRCS}
${MULTIROUND_HEADR}
)
target_link_libraries(libMultiRound
Qt6::Widgets
Qt6::Network
Qt6::Core)
multiround/viewmodels/cancelroundviewmodel.h
#ifndef __CANCEL_ROUND_VIEWMODEL__
#define __CANCEL_ROUND_VIEWMODEL__
#include <QString>
#include <QJsonObject>
struct CancelRoundViewModel {
long int m_RoundId;
long int m_GameId;
QJsonObject toJson() {
QJsonObject retVal;
retVal.insert("roundId", QString::number(m_RoundId));
retVal.insert("gameId", QString::number(m_GameId));
return retVal;
}
};
#endif
multiround/communicationobjects/basiscommobj.h
#ifndef __BASIS_COM_OBJ__
#define __BASIS_COM_OBJ__
#include <QObject>
#include <QJsonObject>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QSettings>
#include <QWidget>
#include "globaldata.h"
class BasisCommObj : public QObject {
Q_OBJECT
public:
BasisCommObj(const QString& requestPath, const QString& actionName):
m_RequestPath(requestPath), m_ActionName(actionName), m_ParentWidget(nullptr) {
connect( m_NetworkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslErrorOccured(QNetworkReply*,QList<QSslError>)));
}
virtual ~BasisCommObj();
bool makeRequestBasis(bool withToken, bool fromFinishedSlot = false);
virtual bool validateReply(const QJsonObject& reply) = 0;
protected:
BasisCommObj() {}
public slots:
virtual void errorRequest(QNetworkReply::NetworkError code);
virtual void finishedRequest();
void sslErrorOccured(QNetworkReply* reply, const QList<QSslError>& errors);
protected:
bool finishRequestHelper(QJsonObject& retJson);
bool checkInt(const QJsonValue& jsonValue);
bool checkLong(const QString& stringVal);
protected:
std::vector<QNetworkReply*> m_ReplyObjectVector; //TODO: we don't need this
QNetworkReply* m_ReplyObject = nullptr;
QString m_RequestPath;
QString m_ActionName;
QJsonObject m_RequestData;
QWidget* m_ParentWidget;
QNetworkAccessManager* m_NetworkManager;
QSettings* m_Settings;
bool m_IsSinglePlayer = true;
GlobalData* m_GlobalData;
};
#endif
multiround/communicationobjects/basiscommobj.cpp
#include "basiscommobj.h"
#include <cmath>
#include <QMessageBox>
#include <QDebug>
#include <QJsonValue>
#include "communicationtools.h"
BasisCommObj::~BasisCommObj()
{
if (m_ReplyObject != nullptr)
delete m_ReplyObject;
}
//TODO: add timer to control maximum duration of request
bool BasisCommObj::makeRequestBasis(bool withToken, bool fromFinishedSlot)
{
if (m_IsSinglePlayer) {
//() << "makeRequestBasis in single player modus";
return false;
}
if ( m_ReplyObject != nullptr && m_ReplyObject->isRunning())
return false;
if ( m_ReplyObject!= nullptr) {
if (!fromFinishedSlot) {
delete m_ReplyObject;
} else { //cannot delete the reply object from finished slot
m_ReplyObjectVector.push_back(m_ReplyObject); //TODO: I don't really need this
m_ReplyObjectVector[m_ReplyObjectVector.size() - 1]->deleteLater();
}
}
if (withToken) {
m_ReplyObject = CommunicationTools::buildPostRequestWithAuth(m_RequestPath, m_Settings->value("multiplayer/serverpath").toString(), m_RequestData, m_GlobalData->m_UserData.m_AuthToken, m_NetworkManager);
} else {
m_ReplyObject = CommunicationTools::buildPostRequest(m_RequestPath, m_Settings->value("multiplayer/serverpath").toString(), m_RequestData, m_NetworkManager);
}
connect(m_ReplyObject, &QNetworkReply::finished, this, &BasisCommObj::finishedRequest);
connect(m_ReplyObject, &QNetworkReply::errorOccurred, this, &BasisCommObj::errorRequest);
return true;
}
void BasisCommObj::errorRequest(QNetworkReply::NetworkError code)
{
//qDebug() << "Error 1";
if (m_IsSinglePlayer) {
// qDebug() << "errorRequest in single player modus";
return;
}
CommunicationTools::treatCommunicationError(m_ActionName, m_ReplyObject, m_ParentWidget);
}
void BasisCommObj::finishedRequest()
{
//() << "Finished Request 1";
QJsonObject retJson;
if (!finishRequestHelper(retJson))
return;
}
bool BasisCommObj::finishRequestHelper(QJsonObject& retJson)
{
if (m_IsSinglePlayer) {
//qDebug() << "finishRequestHelper in single player modus";
return false;
}
if (m_ReplyObject == nullptr)
return false;
if (m_ReplyObject->error() != QNetworkReply::NoError) {
return false;
}
QByteArray reply = m_ReplyObject->readAll();
QString replyQString(reply);
//qDebug() << replyQString;
retJson = CommunicationTools::objectFromString(replyQString);
if (!validateReply(retJson)) {
QMessageBox msgBox(m_ParentWidget);
msgBox.setText(m_ActionName + " reply was not recognized");
msgBox.exec();
return false;
}
return true;
}
bool BasisCommObj::checkInt(const QJsonValue& jsonValue) {
double val = jsonValue.toDouble();
double fractpart, intpart;
fractpart = modf(val , &intpart);
if (fractpart < 0.000001)
return true;
return false;
}
bool BasisCommObj::checkLong(const QString& stringVal)
{
bool ok = false;
stringVal.toLong(&ok, 10);
return ok;
}
void BasisCommObj::sslErrorOccured(QNetworkReply* reply, const QList<QSslError>& errors)
{
qDebug() << "Ssl errors";
for (auto error : errors) {
qDebug() << error.errorString();
}
}
multiround/communicationobjects/cancelroundcommobj.h
#ifndef __CANCEL_ROUND_COMMOBJ__
#define __CANCEL_ROUND_COMMOBJ__
#include "basiscommobj.h"
#include "viewmodels/cancelroundviewmodel.h"
class MultiplayerRound;
class CancelRoundCommObj : public BasisCommObj {
Q_OBJECT
public:
CancelRoundCommObj(const QString& requestPath, const QString& actionName):
BasisCommObj(requestPath, actionName) {}
bool makeRequest();
bool validateReply(const QJsonObject& retJson) override;
protected:
CancelRoundCommObj() {}
public slots:
void finishedRequest() override;
signals:
void roundCancelled();
private:
CancelRoundViewModel prepareViewModel();
private:
MultiplayerRound* m_MultiRound;
friend class CancelRoundCommObjTest;
};
#endif
multiround/communicationobjects/cancelroundcommobj.cpp
#include "cancelroundcommobj.h"
#include <QMessageBox>
#include "viewmodels/cancelroundviewmodel.h"
#include "multiplayerround.h"
bool CancelRoundCommObj::makeRequest()
{
if (m_IsSinglePlayer) {
//qDebug() << "makeRequestBasis in single player modus";
return false;
}
if (m_GlobalData->m_UserData.m_UserName.isEmpty()) {
if (m_ParentWidget != nullptr) { //nullptr is in tests
QMessageBox msgBox(m_ParentWidget);
msgBox.setText("No user logged in");
msgBox.exec();
}
return false;
}
m_RequestData = prepareViewModel().toJson();
makeRequestBasis(true);
return true;
}
void CancelRoundCommObj::finishedRequest()
{
QJsonObject retJson;
if (!finishRequestHelper(retJson))
return;
//m_MultiRound->setRoundCancelled();
emit roundCancelled();
}
bool CancelRoundCommObj::validateReply(const QJsonObject& reply) {
return (reply.contains("roundId"));
}
CancelRoundViewModel CancelRoundCommObj::prepareViewModel() {
CancelRoundViewModel cancelRoundData;
cancelRoundData.m_RoundId = m_GlobalData->m_GameData.m_RoundId;
cancelRoundData.m_GameId = m_GlobalData->m_GameData.m_GameId;
return cancelRoundData;
}
multiround/communicationtools.h
#ifndef _COMMUNICATION_TOOLS__
#define _COMMUNICATION_TOOLS__
#include <QString>
#include <QNetworkReply>
#include <QJsonObject>
#include <QWidget>
class CommunicationTools {
private:
static QString localTestServerPath;
public:
static QNetworkReply* buildPostRequest(const QString& routePath, const QString& serverPath, const QJsonObject& jsonObject, QNetworkAccessManager* networkManager);
static QNetworkReply* buildPostRequestWithAuth(const QString& routePath, const QString& serverPath, const QJsonObject& jsonObject, const QByteArray& authToken, QNetworkAccessManager* networkManager);
static QJsonObject objectFromString(const QString& in);
static void treatCommunicationError(const QString& actionName, QNetworkReply* reply, QWidget* parentWidget);
};
#endif
multiround/communicationtools.cpp
#include "communicationtools.h"
#include <QJsonDocument>
#include <QMessageBox>
QString CommunicationTools::localTestServerPath = "";
QNetworkReply * CommunicationTools::buildPostRequestWithAuth(const QString& routePath, const QString& serverPath, const QJsonObject& jsonObject, const QByteArray& authToken, QNetworkAccessManager* networkManager)
{
QString requestPath = serverPath;
if (serverPath.isEmpty())
requestPath = localTestServerPath;
//qDebug() << "request to " << requestPath ;
QUrl loginRequestUrl = QUrl(requestPath + routePath);
QNetworkRequest request(loginRequestUrl);
request.setRawHeader("Content-Type", "application/fhir+json");
request.setRawHeader(QByteArray("Authorization"), authToken);
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setProtocol(QSsl::TlsV1_3);
request.setSslConfiguration(config);
//qDebug() << "prepare request" ;
QByteArray data = QJsonDocument(jsonObject).toJson();
//qDebug() << "prepare json";
return networkManager->post(request, data);
}
QNetworkReply * CommunicationTools::buildPostRequest(const QString& routePath, const QString& serverPath, const QJsonObject& jsonObject, QNetworkAccessManager* networkManager)
{
QString requestPath = serverPath;
if (serverPath.isEmpty())
requestPath = localTestServerPath;
//qDebug() << "request to " << requestPath ;
QUrl loginRequestUrl = QUrl(requestPath + routePath);
QNetworkRequest request(loginRequestUrl);
request.setRawHeader("Content-Type", "application/fhir+json");
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setProtocol(QSsl::TlsV1_3);
request.setSslConfiguration(config);
//qDebug() << "prepare request" ;
QByteArray data = QJsonDocument(jsonObject).toJson();
//qDebug() << "prepare json";
return networkManager->post(request, data);
}
QJsonObject CommunicationTools::objectFromString(const QString& in)
{
QJsonObject obj;
QJsonDocument doc = QJsonDocument::fromJson(in.toUtf8());
// check validity of the document
if(!doc.isNull()) {
if(doc.isObject()) {
obj = doc.object();
} else {
//qDebug() << "Document is not an object";
}
} else {
//qDebug() << "Invalid JSON...\n" << in;
}
return obj;
}
void CommunicationTools::treatCommunicationError(const QString& actionName, QNetworkReply* reply, QWidget* parentWidget) {
QByteArray replyBA = reply->readAll();
QString registrationReplyQString(replyBA);
QMessageBox msgBox(parentWidget);
msgBox.setText("Error when " + actionName + " " + reply->errorString() + "\n" + registrationReplyQString);
msgBox.exec();
}
multiround/multiplayerround.h
#ifndef __MULTIPLAYER_ROUND__
#define __MULTIPLAYER_ROUND__
#if defined MAKE_MULTIPLAYERROUND_LIB
#define MULTIPLAYER_EXPORT Q_DECL_EXPORT
#else
#define MULTIPLAYER_EXPORT Q_DECL_IMPORT
#endif
#include <QObject>
#include <QNetworkAccessManager>
#include <QSettings>
#include <QJsonObject>
#include <QNetworkReply>
#include <QTimer>
#include <QWidget>
#include "gameinfo.h"
#include "globaldata.h"
#include "communicationobjects/cancelroundcommobj.h"
class MULTIPLAYER_EXPORT MultiplayerRound : public QObject/*, public AbstractPlaneRound*/ {
Q_OBJECT
private:
CancelRoundCommObj* m_CancelRoundCommObj;
public:
MultiplayerRound();
~MultiplayerRound();
void cancelRound();
};
#endif
multiround/multiplayerround.cpp
#include "multiplayerround.h"
#include <QMessageBox>
#include <QJsonArray>
#include "communicationtools.h"
MultiplayerRound::MultiplayerRound()
{
m_CancelRoundCommObj = new CancelRoundCommObj("/round/cancel", "cancelling round ");
//connect(m_CancelRoundCommObj, &CancelRoundCommObj::roundCancelled, this, &MultiplayerRound::roundWasCancelled);
}
MultiplayerRound::~MultiplayerRound()
{
delete m_CancelRoundCommObj;
}
void MultiplayerRound::cancelRound()
{
m_CancelRoundCommObj->makeRequest();
}
multiround/gameinfo.h
#ifndef __GAME_INFO__
#define __GAME_INFO__
class GameInfo {
private:
bool m_IsSinglePlayer = false;
public:
GameInfo(bool isMultiplayer) { m_IsSinglePlayer = !isMultiplayer; }
void setSinglePlayer(bool singlePlayer) {
m_IsSinglePlayer = singlePlayer;
}
bool getSinglePlayer() {
return m_IsSinglePlayer;
}
};
multiround/globaldata.h
#ifndef __GLOBAL_DATA__
#define __GLOBAL_DATA__
#include "globaluserdata.h"
#include "globalgamedata.h"
struct GlobalData {
GlobalGameData m_GameData;
GlobalUserData m_UserData;
public:
void reset() {
m_GameData.reset();
m_UserData.reset();
}
};
multiround/globalgamedata.h
#ifndef __GLOBAL_GAME_DATA__
#define __GLOBAL_GAME_DATA__
#include <QString>
struct GlobalGameData {
long int m_GameId;
long int m_RoundId;
long int m_UserId;
long int m_OtherUserId;
QString m_GameName;
QString m_OtherUsername;
public:
void reset() {
m_GameId = 0;
m_RoundId = 0;
m_UserId = 0;
m_OtherUserId = 0;
m_GameName.clear();
m_OtherUsername.clear();
}
};
#endif
multiround/globaluserdata.h
#ifndef __GLOBAL_USER_DATA__
#define __GLOBAL_USER_DATA__
#include <QString>
#include <QByteArray>
struct GlobalUserData {
QString m_UserName;
QString m_UserPassword;
QByteArray m_AuthToken;
long int m_UserId;
public:
void reset() {
m_AuthToken = QByteArray(); //TODO: token expires some times
m_UserName = QString();
m_UserPassword = QString();
m_UserId = 0;
}
};
#endif
tests/CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
project (commobjtest)
cmake_policy(SET CMP0020 NEW)
cmake_policy(SET CMP0043 NEW)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../multiround/
${CMAKE_CURRENT_SOURCE_DIR}/../multiround/communicationobjects
${CMAKE_CURRENT_SOURCE_DIR}/../multiround/viewmodels
#${CMAKE_CURRENT_SOURCE_DIR}/../bcrypt/
${Qt6Widgets_INCLUDE_DIRS}
${Qt6Network_INCLUDE_DIRS}
${Qt6Core_INCLUDE_DIRS}
${Qt6Network_INCLUDE_DIRS})
set(TEST_HEADR
cancelroundcommobjtest.h
#logincommobjtest.h
)
set(TEST_SRCS
cancelroundcommobjtest.cpp
#logincommobjtest.cpp
main.cpp)
enable_testing(true)
add_executable(commobjtest ${TEST_HEADR} ${TEST_SRCS})
add_test(NAME commobjtest COMMAND commobjtest)
target_link_libraries(commobjtest
libMultiRound
#libbcrypt
#libCommon
Qt6::Test
Qt6::Widgets
Qt6::Network
Qt6::Core)
tests/main.cpp
#include "cancelroundcommobjtest.h"
int main(int argc, char** argv)
{
int status = 0;
{
CancelRoundCommObjTest tc;
status |= QTest::qExec(&tc, argc, argv);
}
return status;
}
tests/cancelroundcommobjtest.h
#ifndef __CANCEL_ROUND_COMMOBJ_TEST__
#define __CANCEL_ROUND_COMMOBJ_TEST__
#include <QObject>
#include <QTest>
#include "cancelroundcommobj.h"
class CancelRoundCommObjTest : public QObject {
Q_OBJECT
private:
CancelRoundCommObj m_CommObj;
private slots:
void initTestCase();
void SinglePlayerTest();
void NoUserLoggedInTest();
void PrepareViewModelTest();
void cleanupTestCase();
};
#endif
tests/cancelroundcommobjtest.cpp
#include "cancelroundcommobjtest.h"
#include <QTest>
void CancelRoundCommObjTest::initTestCase()
{
qDebug("CancelRoundCommObjTest starts ..");
}
void CancelRoundCommObjTest::SinglePlayerTest()
{
m_CommObj.m_IsSinglePlayer = true;
QVERIFY2(m_CommObj.makeRequest() == false, "CancelRoundCommObj should abort if single player game");
}
void CancelRoundCommObjTest::NoUserLoggedInTest()
{
m_CommObj.m_IsSinglePlayer = false;
m_CommObj.m_ParentWidget = nullptr;
GlobalData* gd = new GlobalData();
gd->m_UserData.m_UserName = "";
m_CommObj.m_GlobalData = gd;
QVERIFY2(m_CommObj.makeRequest() == false, "Cannot cancel round without being logged in");
}
void CancelRoundCommObjTest::PrepareViewModelTest()
{
GlobalData* gd = new GlobalData();
gd->m_UserData.m_UserName = "testUserName";
gd->m_GameData.m_RoundId = 123L;
gd->m_GameData.m_GameId = 234L;
m_CommObj.m_GlobalData = gd;
CancelRoundViewModel viewModel = m_CommObj.prepareViewModel();
QVERIFY2(viewModel.m_GameId == 234L, "GameId was not copied to the view model");
QVERIFY2(viewModel.m_RoundId == 123L, "RoundId was not copied to the view model");
}
void CancelRoundCommObjTest::cleanupTestCase()
{
qDebug("CancelRoundCommObjTest ends ..");
}
CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
project (Planes)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1)
find_package(Qt6 COMPONENTS Core Widgets Quick Network Test)
add_subdirectory(multiround)
add_subdirectory(tests)
Does has an idea what this can be? How come this works on the Microsoft compiler and on MinGW not ?
Related
I have a problem with this situation (underneath) in the console.
The data is lost after passing twice in my method called in main.ccp after making the MyClass object.
main.ccp
#include <QCoreApplication>
#include <QDebug>
#include <iostream>
#include <myclass.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass* myClass = new MyClass();
qDebug() << "Debug part 1";
myClass->method();
qDebug() << "Debug part 2";
myClass->method();
return a.exec();
}
The result in console:
Debug part 1
0
1 ".." "0 Bytes" "26.03.2022 08:21:13"
2 "stephane/" "0 Bytes" "26.04.2022 19:48:04"
3 ".localized" "0 Bytes" "26.03.2022 08:21:13"
4 "Shared/" "0 Bytes" "26.03.2022 08:21:13"
Debug part 2
0
The sources files:
myclass.h
myclass.ccp
entrys.h
entrys.ccp
entry.h
entry.ccp
myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QString>
#include <QDateTime>
#include "entrys.h"
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr);
void method();
signals:
private:
Entrys* entrys;
};
#endif // MYCLASS_H
myclass.ccp
#include "myclass.h"
#include <iostream>
#include "myclass.h"
#include "entry.h"
#include "entrys.h"
MyClass::MyClass(QObject *parent) : QObject(parent) {
this->entrys = new Entrys();
try {
this->entrys->setDir("/Users/","L");
} catch(ErrDirNotFound &e) {
qDebug() << e.description << " " << e.what();
}
}
void MyClass::method() {
int i = 0;
qDebug() << i;
foreach(Entry *v, this->entrys->getEntrys("L")) {
i++;
qDebug() << i << v->getName() << " " << v->getSizeString(2) << " " << v->getDateLastChangeString();
}
}
entrys.h
#ifndef ENTRYS_H
#define ENTRYS_H
#include <QObject>
#include "entry.h"
struct ErrDirNotFound: public std::exception {
QString description;
const char *what() const throw() {
return "Directory not found";
}
};
class Entrys : public QObject
{
Q_OBJECT
public:
explicit Entrys(QObject *parent = nullptr);
void setDir(QString dir, QString side);
QVector<Entry*> getEntrys(QString side);
Entry* getEntry(QString side, QString key);
QString getPath(QString side);
protected:
signals:
private:
QHash<QString, QString> hash_path;
QHash<QString, QVector<Entry*>> hash_side_entry;
void setList(QString side);
};
#endif // ENTRYS_H
entrys.ccp
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <QDebug>
#include <iostream>
#include <QDateTime>
#include <QProcess>
#include "entry.h"
#include "entrys.h"
Entrys::Entrys(QObject *parent)
: QObject{parent}
{
}
void Entrys::setList(QString side) {
QVector<Entry*> vec_entry;
QString path = this->getPath(side);
QByteArray path_ba = path.toLocal8Bit();
const char* path_cstr = path_ba.constData();
struct dirent *lecture;
DIR *dir;
struct stat buf;
QString currentPath;
int row = 0;
dir = opendir(path_cstr);
if (dir == NULL) {
ErrDirNotFound e;
QString description = "Path " + path + " don't exist !";
e.description = description;
throw e;
}
while ((lecture = readdir(dir)) != NULL) {
if (strcmp(lecture->d_name, ".") != 0) {
currentPath = path + lecture->d_name;
QByteArray path_qb = currentPath.toLocal8Bit();
const char *charCurrentPath = path_qb.constData();
if ((stat(charCurrentPath, &buf)) == -1) {
qCritical() << "stat" << currentPath;
}
int size = buf.st_size;
QDateTime modif = QDateTime::fromSecsSinceEpoch(buf.st_mtime);
Entry *entry = new Entry();
if (!strcmp(lecture->d_name, "..")) {
if (this->getPath(side) != "/") {
entry->setValue(lecture->d_name, 0, modif, 0);
}
} else {
if (S_ISDIR(buf.st_mode)) {
QString qstringTemp = lecture->d_name;
qstringTemp += "/";
entry->setValue(qstringTemp, 0, modif, buf.st_mode);
} else {
entry->setValue(lecture->d_name, size, modif, buf.st_mode);
}
}
vec_entry.append(entry);
row++;
}
}
delete lecture;
closedir(dir);
this->hash_side_entry.insert(side, vec_entry);
}
void Entrys::setDir(QString dir, QString side) {
this->hash_path.insert(side, dir);
this->setList(side);
}
QVector<Entry*> Entrys::getEntrys(QString side) {
return this->hash_side_entry.take(side);
}
QString Entrys::getPath(QString side) {
return this->hash_path[side];
}
Entry* Entrys::getEntry(QString side, QString key) {
QVector<Entry*> entry = this->getEntrys(side);
for (int i = 0; i < entry.length(); i++) {
if (entry[i]->getName() == key) {
return entry[i];
}
}
return nullptr;
}
entry.h
#ifndef ENTRY_H
#define ENTRY_H
#include <QObject>
#include <QString>
#include <QDateTime>
class Entry : public QObject
{
Q_OBJECT
public:
explicit Entry(QObject *parent = nullptr);
Entry(QString name, int size_file, QDateTime date_last_change, mode_t mode);
void setValue(QString name, int size_file, QDateTime date_last_change, mode_t mode);
QString getName();
QString getSizeString(int decimals);
QString getDateLastChangeString();
signals:
private:
QString name;
int size_file;
QDateTime date_last_change;
mode_t mode;
};
#endif // ENTRY_H
entry.ccp
#include <QDateTime>
#include "entry.h"
Entry::Entry(QObject *parent)
: QObject{parent}
{
}
Entry::Entry(QString name, int size_file, QDateTime date_last_change, mode_t mode)
{
this->name = name;
this->size_file = size_file;
this->date_last_change = date_last_change;
this->mode = mode;
}
void Entry::setValue(QString name, int size_file, QDateTime date_last_change, mode_t mode)
{
this->name = name;
this->size_file = size_file;
this->date_last_change = date_last_change;
this->mode = mode;
}
QString Entry::getName()
{
return this->name;
}
QString Entry::getSizeString(int decimals) {
int bytes = this->size_file;
if (bytes == 0) return "0 Bytes";
const int K = 1024;
const QStringList SIZES = { "Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
const int I = std::floor((std::log(bytes) / std::log(K)));
int dm = decimals < 0 ? 0 : decimals;
if (I == 0) dm = 0;
return QString::number((bytes / std::pow(K, I)),'f', dm) + " " + SIZES[I];
}
QString Entry::getDateLastChangeString() {
return this->date_last_change.toString("dd.MM.yyyy hh:mm:ss");
}
Tracking through your code by eye, I find this concerning:
QVector<Entry*> Entrys::getEntrys(QString side) {
return this->hash_side_entry.take(side);
}
A bit of googling indicates that QHash's take "Removes the item with the key from the hash and returns the value associated with it." So your getEntrys is modifying your hash_side_entry - taking data out of it. Thus when your second call to method ends up calling getEntrys a second time, there's nothing in hash_side_entry anymore.
How to catch incorrect link errors? For example:
ftp://cddis.gsfc.nasa.gov/pub/slr/data/npt_crd/gracea/2010/gracea_20100101.npt.Z
-- this is incorrect link
Why doesn't any error signal work? Or How to determine the correct links? Please, write an example of correct work with errors.
#ifndef WIDGET_H
#define WIDGET_H
#include "Downloader.h"
#include <QWidget>
#include <QNetworkReply>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void onDownloadButtonClicked();
void onSelectTargetFolderButtonClicked();
void onCancelButtonClicked();
void onUpdateProgress(qint64 bytesReceived, qint64 bytesTotal);
void onResult(QNetworkReply *reply);
private:
Ui::Widget *ui;
Downloader m_downloader;
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
#include <QFileDialog>
#include <QStandardPaths>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->downloadPushButton, &QPushButton::clicked, this, &Widget::onDownloadButtonClicked);
connect(ui->selectTargetFolderPushButton, &QPushButton::clicked, this, &Widget::onSelectTargetFolderButtonClicked);
connect(ui->cancelPushButton, &QPushButton::clicked, this, &Widget::onCancelButtonClicked);
connect(&m_downloader, &Downloader::updateDownloadProgress, this, &Widget::onUpdateProgress);
connect(&m_downloader.manager, &QNetworkAccessManager::finished, this, &Widget::onResult);
}
Widget::~Widget()
{
delete ui;
}
void Widget::onDownloadButtonClicked()
{
m_downloader.get(ui->targetFolderLineEdit->text(), ui->urlLineEdit->text());
}
void Widget::onSelectTargetFolderButtonClicked()
{
QString targetFolder = QFileDialog::getExistingDirectory(this,
tr("Select folder"),
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
ui->targetFolderLineEdit->setText(targetFolder);
}
void Widget::onCancelButtonClicked()
{
m_downloader.cancelDownload();
ui->downloadProgressBar->setMaximum(100);
ui->downloadProgressBar->setValue(0);
}
void Widget::onUpdateProgress(qint64 bytesReceived, qint64 bytesTotal)
{
ui->downloadProgressBar->setMaximum(bytesTotal);
ui->downloadProgressBar->setValue(bytesReceived);
}
void Widget::onResult(QNetworkReply *reply)
{
if (reply->error()) {
qDebug() << "Error";
qDebug() << reply->error();
} else {
qDebug() << "Done";
}
}
Downloader.h
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QNetworkAccessManager>
#include <QNetworkReply>
//class QNetworkReply;
class QFile;
class Downloader : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
explicit Downloader(QObject* parent = nullptr);
bool get(const QString& targetFolder, const QUrl& url);
QNetworkReply* currentReply {nullptr};
QNetworkAccessManager manager;
QString fullName;
public slots:
void cancelDownload();
signals:
void updateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private slots:
void onReadyRead();
void onReply(QNetworkReply* reply);
void errorSlot(QNetworkReply::NetworkError er);
private:
QFile* m_file {nullptr};
};
#endif // DOWNLOADER_H
Downloader.cpp
#include "Downloader.h"
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QFile>
#include <QDir>
Downloader::Downloader(QObject* parent) :
BaseClass(parent)
{
connect(&manager, &QNetworkAccessManager::finished, this, &Downloader::onReply);
}
bool Downloader::get(const QString& targetFolder, const QUrl& url)
{
qDebug() << "get";
if (targetFolder.isEmpty() || url.isEmpty())
{
return false;
}
fullName = targetFolder + QDir::separator() + url.fileName();
m_file = new QFile(fullName);
if (!m_file->open(QIODevice::WriteOnly))
{
delete m_file;
m_file = nullptr;
return false;
}
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
currentReply = manager.get(request);
connect(currentReply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
this, &Downloader::errorSlot);
connect(currentReply, &QNetworkReply::readyRead, this, &Downloader::onReadyRead);
connect(currentReply, &QNetworkReply::downloadProgress, this, &Downloader::updateDownloadProgress);
return true;
}
void Downloader::onReadyRead()
{
qDebug() << "onReadyRead";
if (m_file)
{
m_file->write(currentReply->readAll());
}
}
void Downloader::cancelDownload()
{
qDebug() << "cancelDownload";
if (currentReply)
{
currentReply->abort();
}
}
void Downloader::onReply(QNetworkReply* reply)
{
qDebug() << "onReply";
if (reply->error() == QNetworkReply::NoError)
{
m_file->flush();
m_file->close();
}
else
{
m_file->remove();
}
delete m_file;
m_file = nullptr;
reply->deleteLater();
}
void Downloader::errorSlot(QNetworkReply::NetworkError er)
{
qDebug() << er;
}
I've successfully redirected qDebug() output to a QTextEdit widget. For several reasons, I'd like every qDebug() message to be included in an emitted signal. One reason is that the object which shall receive the output isn't available. Another reason is that I want to redirect the output to different objects depending on which activity is active at the moment (using connect/disconnect of the signal to different slots).
I've made a working example code that redirects qDebug to a QTextEdit widget. Can someone please help me to get this code to emit a signal which includes the qDebug message?
I'm not sure if its possible to have Q_DebugStream emit a signal (I've tried and failed to make a Qt Class out of it).
It must be possible to pass a pointer to a function/slot instead of a pointer to a QTextEdit when calling Q_DebugStream, but I'm not sure how this is done.
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void logSignal(QString);
public slots:
void logSlot(QString);
private:
QTextEdit *logView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "q_debugstream.h"
#include <QGridLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
setCentralWidget(mainWidget);
logView = new QTextEdit;
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(logView,0,0,1,1);
mainWidget->setLayout(mainLayout);
connect(this, SIGNAL(logSignal(QString)),
this, SLOT(logSlot(QString)));
emit logSignal("Message from a signal\n");
new Q_DebugStream(std::cout, logView); //Redirect Console output to QTextEdit
Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
qDebug() << "DEBUG MODE ACTIVE";
}
MainWindow::~MainWindow(){}
void MainWindow::logSlot(QString log) {
logView->append(log);
}
q_debugstream.h
//As per forum:
//http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html
//A couple of lines added to ensure newlines go between each call.
//Thanks, James!
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QTextEdit>
class Q_DebugStream : public std::basic_streambuf<char>
{
public:
Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
{
log_window = text_edit;
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~Q_DebugStream()
{
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler(){
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
{
std::cout << msg.toStdString().c_str();
}
protected:
//This is called when a std::endl has been inserted into the stream
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
log_window->append("");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
QString str(p);
if(str.contains("\n")){
QStringList strSplitted = str.split("\n");
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++){
log_window->append(strSplitted.at(i));
log_window->append("\n");
}
}else{
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (str);
log_window->insertPlainText ("\n");
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
QTextEdit* log_window;
};
#endif // Q_DEBUGSTREAM_H
When application is started, I get both messages in my QTextEdit:
"Message from a signal"
"DEBUG MODE ACTIVE"
(This answer extracted from an edit to the question - now rolled back).
Here's how I solved this:
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void logSignal(QString);
public slots:
void logSlot(QString);
private:
void dbgMsg(QString);
QTextEdit *logView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "q_debugstream.h"
#include <QGridLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
setCentralWidget(mainWidget);
logView = new QTextEdit;
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(logView,0,0,1,1);
mainWidget->setLayout(mainLayout);
connect(this, SIGNAL(logSignal(QString)),
this, SLOT(logSlot(QString)));
emit logSignal("Now call Q_DebugStream");
//Redirect qDebug() output to dbgMsg(QString)
new Q_DebugStream(std::cout, this, &MainWindow::dbgMsg);
Q_DebugStream::registerQDebugMessageHandler();
qDebug() << "Debug message";
qWarning() << "Warning!";
qCritical() << "Critical issue!";
qInfo() << "Information";
qDebug() << "This\nis\na\nlong\none.";
}
MainWindow::~MainWindow(){}
void MainWindow::logSlot(QString log) {
logView->append(log);
}
void MainWindow::dbgMsg(QString log) {
emit logSignal(log);
}
q_debugstream.h
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QString>
#include "mainwindow.h"
class Q_DebugStream : public std::basic_streambuf<char> {
public:
Q_DebugStream(std::ostream &stream, MainWindow* obj, void (MainWindow::*dbgMsgPtr)(QString log)): m_stream(stream) {
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
msgObj = obj;
msgHandler = dbgMsgPtr;
}
~Q_DebugStream() {
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler() {
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QString message = msg;
switch (type) {
case QtDebugMsg:
message.prepend("qDbg(): ");
break;
case QtWarningMsg:
message.prepend("qWarn(): ");
break;
case QtCriticalMsg:
message.prepend("qCrit(): ");
break;
case QtInfoMsg:
message.prepend("qInfo(): ");
break;
case QtFatalMsg:
message.prepend("qFatal(): ");
abort();
break;
}
message.append(" (" + QString::fromUtf8(context.file) + ")");
message.append(" line: " + QString::number(context.line));
std::cout << message.toStdString().c_str();
}
protected:
//This is called when a std::endl has been inserted into the stream
virtual int_type overflow(int_type v) {
if (v == '\n') {
(msgObj->*msgHandler)("\n");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n) {
QString str(p);
if(str.contains("\n")) {
QStringList strSplitted = str.split("\n");
(msgObj->*msgHandler)(strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++) {
(msgObj->*msgHandler)("\\ " + strSplitted.at(i));
}
} else {
(msgObj->*msgHandler)(str);
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
MainWindow* msgObj;
void (MainWindow::*msgHandler)(QString);
};
#endif // Q_DEBUGSTREAM_H
When application is started, I get these messages in my QTextEdit:
Now call Q_DebugStream
qDbg(): Debug message (..\qDebugFetch\mainwindow.cpp) line: 25
qWarn(): Warning! (..\qDebugFetch\mainwindow.cpp) line: 26
qCrit(): Critical issue! (..\qDebugFetch\mainwindow.cpp) line: 27
qInfo(): Information (..\qDebugFetch\mainwindow.cpp) line: 28
qDbg(): This
\ is
\ a
\ long
\ one. (..\qDebugFetch\mainwindow.cpp) line: 29
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I know this question as been multiple times . I have browsed and trying for 5 days to link my C++ signal to a QML slot via Connections in QML . Here is my code at the moment and I don't understand why I always get :Cannot assign to non-existent property "ondashsetupChanged"
Please tell me what i am doing wrong ? The complete code is here :
https://github.com/BastianGschrey/PowerTune/tree/Simplification
Here is my code:
my main.cpp :
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtQml>
#include "connect.h"
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QApplication app(argc, argv);
app.setOrganizationName("Power-Tune");
app.setOrganizationDomain("power-tune.org");
app.setApplicationName("PowerTune");
QQmlApplicationEngine engine;
qmlRegisterType<Connect>("com.powertune", 1, 0, "ConnectObject");
engine.rootContext()->setContextProperty("Connect", new Connect(&engine));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
connect.cpp :
#include "datalogger.h"
#include "connect.h"
#include "calculations.h"
#include "sensors.h"
#include "AdaptronicSelect.h"
#include "AdaptronicCAN.h"
#include "Apexi.h"
#include "HaltechCAN.h"
#include "Nissanconsult.h"
#include "obd.h"
#include "AdaptronicCAN.h"
#include "HaltechCAN.h"
#include "Apexi.h"
#include "AdaptronicSelect.h"
#include "dashboard.h"
#include "serialport.h"
#include "appsettings.h"
#include "gopro.h"
#include "gps.h"
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QByteArrayMatcher>
#include <QProcess>
int ecu; //0=apex, 1=adaptronic;2= OBD; 3= Dicktator ECU
int logging; // 0 Logging off , 1 Logging to file
int connectclicked =0;
QByteArray checksumhex;
QByteArray recvchecksumhex;
Connect::Connect(QObject *parent) :
QObject(parent),
m_serialport(Q_NULLPTR),
m_dashBoard(Q_NULLPTR),
m_gopro(Q_NULLPTR),
m_gps(Q_NULLPTR),
m_adaptronicselect(Q_NULLPTR),
m_apexi(Q_NULLPTR),
m_nissanconsult(Q_NULLPTR),
m_OBD(Q_NULLPTR),
m_sensors(Q_NULLPTR),
m_haltechCANV2(Q_NULLPTR),
m_adaptronicCAN(Q_NULLPTR),
m_datalogger(Q_NULLPTR),
m_calculations(Q_NULLPTR)
{
getPorts();
m_dashBoard = new DashBoard(this);
m_appSettings = new AppSettings(this);
m_gopro = new GoPro(this);
m_gps = new GPS(m_dashBoard, this);
m_adaptronicselect= new AdaptronicSelect(m_dashBoard, this);
m_apexi= new Apexi(m_dashBoard, this);
m_nissanconsult = new Nissanconsult(m_dashBoard, this);
m_OBD = new OBD(m_dashBoard, this);
m_sensors = new Sensors(m_dashBoard, this);
m_haltechCANV2 = new HaltechCAN(m_dashBoard, this);
m_adaptronicCAN = new AdaptronicCAN(m_dashBoard, this);
m_datalogger = new datalogger(m_dashBoard, this);
m_calculations = new calculations(m_dashBoard, this);
QQmlApplicationEngine *engine = dynamic_cast<QQmlApplicationEngine*>( parent );
if (engine == Q_NULLPTR)
return;
engine->rootContext()->setContextProperty("Dashboard", m_dashBoard);
engine->rootContext()->setContextProperty("AppSettings", m_appSettings);
engine->rootContext()->setContextProperty("GoPro", m_gopro);
engine->rootContext()->setContextProperty("GPS", m_gps);
engine->rootContext()->setContextProperty("Nissanconsult",m_nissanconsult);
engine->rootContext()->setContextProperty("Sens", m_sensors);
engine->rootContext()->setContextProperty("Logger", m_datalogger);
}
Connect::~Connect()
{
}
void Connect::checkifraspberrypi()
{
QString path = "/sys/class/backlight/rpi_backlight/brightness";
if (QFileInfo::exists(path))
{
m_dashBoard->setscreen(true);
}
else
{
m_dashBoard->setscreen(false);
}
}
void Connect::readdashsetup()
{
qDebug()<<"c++ file read";
QString path = "UserDash.txt";// this is just for testing
QFile inputFile(path);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while (!in.atEnd())
{
QString line = in.readLine();
QStringList list = line.split(QRegExp("\\,"));
m_dashBoard->setdashsetup(list);
qDebug()<< list;
}
inputFile.close();
}
}
void Connect::setSreenbrightness(const int &brightness)
{
//This works only on raspberry pi
QFile f("/sys/class/backlight/rpi_backlight/brightness");
//f.close();
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
QTextStream out(&f);
out << brightness;
//qDebug() << brightness;
f.close();
}
void Connect::setUnits(const int &units)
{
switch (units)
{
case 0:
m_dashBoard->setunits("metric");
break;
case 1:
m_dashBoard->setunits("imperial");
break;
default:
break;
}
}
void Connect::setWeight(const int &weight)
{
m_dashBoard->setWeight(weight);
qDebug() << "weight" << m_dashBoard->Weight();
}
void Connect::getPorts()
{
QStringList PortList;
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
PortList.append(info.portName());
}
setPortsNames(PortList);
// Check available ports every 1000 ms
QTimer::singleShot(1000, this, SLOT(getPorts()));
}
//function for flushing all Connect buffers
void Connect::clear() const
{
// m_Connectport->clear();
}
//function to open Connect port
void Connect::openConnection(const QString &portName, const int &ecuSelect)
{
ecu = ecuSelect;
//Apexi
if (ecuSelect == 0)
{
m_apexi->openConnection(portName);
}
//Adaptronic
if (ecuSelect == 1)
{
m_adaptronicselect->openConnection(portName);
}
//OBD
if (ecuSelect == 2)
{
m_OBD->openConnection(portName);
}
//Nissan Consult
if (ecuSelect == 3)
{
m_nissanconsult->LiveReqMsg(1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
m_nissanconsult->openConnection(portName);
}
//Adaptronic ModularCAN protocol
if (ecuSelect == 5)
{
m_adaptronicCAN->openCAN();
}
//Haltech V2 CAN protocol
if (ecuSelect == 6)
{
m_haltechCANV2->openCAN();
}
}
void Connect::closeConnection()
{
//Apexi
if (ecu == 0)
{
m_apexi->closeConnection();
}
//Adaptronic Select
if (ecu == 1)
{
m_adaptronicselect->closeConnection();
}
//OBD
if (ecu == 2)
{
m_OBD->closeConnection();
}
//Nissan Consult
if (ecu == 3)
{
m_nissanconsult->closeConnection();
}
//Adaptronic ModularCAN protocol
if (ecu == 5)
{
m_adaptronicCAN->closeConnection();
}
//Haltech V2 CAN protocol
if (ecu == 6)
{
m_haltechCANV2->closeConnection();
}
}
void Connect::update()
{
m_dashBoard->setSerialStat("Update started");
QProcess *process = new QProcess(this);
connect(process , SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(updatefinished(int, QProcess::ExitStatus)));
process->start("/home/pi/updatePowerTune.sh");
process->waitForFinished(6000000); // 10 minutes time before timeout
}
void Connect::updatefinished(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "code" <<exitCode;
qDebug() << "status" <<exitStatus;
QString fileName = "/home/pi/build/PowertuneQMLGui";
QFile file(fileName);
if(QFileInfo::exists(fileName))
{
m_dashBoard->setSerialStat("Update Successful");
file.close();
}
else
{
m_dashBoard->setSerialStat("Update Unsuccessful");
}
}
connect.h
#ifndef CONNECT_H
#define CONNECT_H
#include <QtSerialPort/QSerialPort>
#include <QObject>
#include <QModbusDataUnit>
#include <QTimer>
#include <QProcess>
#include "calculations.h"
class SerialPort;
class Sensors;
class DashBoard;
class AdaptronicCAN;
class AdaptronicSelect;
class Apexi;
class HaltechCAN;
class Nissanconsult;
class OBD;
class datalogger;
class calculations;
class AppSettings;
class GoPro;
class GPS;
class OBD;
class Connect : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList portsNames READ portsNames WRITE setPortsNames NOTIFY sig_portsNamesChanged)
public:
~Connect();
explicit Connect(QObject *parent = 0);
Q_INVOKABLE void checkifraspberrypi();
Q_INVOKABLE void readdashsetup();
Q_INVOKABLE void setSreenbrightness(const int &brightness);
Q_INVOKABLE void setUnits(const int &units);
Q_INVOKABLE void setWeight(const int &weight);
Q_INVOKABLE void clear() const;
Q_INVOKABLE void openConnection(const QString &portName, const int &ecuSelect);
Q_INVOKABLE void closeConnection();
Q_INVOKABLE void update();
public:
QStringList portsNames() const { return m_portsNames; }
private:
SerialPort *m_serialport;
DashBoard *m_dashBoard;
AppSettings *m_appSettings;
GoPro *m_gopro;
GPS *m_gps;
AdaptronicSelect *m_adaptronicselect;
Apexi *m_apexi;
Nissanconsult* m_nissanconsult;
OBD* m_OBD;
Sensors *m_sensors;
HaltechCAN *m_haltechCANV2;
AdaptronicCAN *m_adaptronicCAN;
datalogger *m_datalogger;
calculations *m_calculations;
QStringList m_portsNames;
QStringList *m_ecuList;
QThread* CALCThread;
QProcess process;
signals:
void sig_portsNamesChanged(QStringList portsNames);
public slots:
void updatefinished(int exitCode, QProcess::ExitStatus exitStatus);
void getPorts();
void setPortsNames(QStringList portsNames)
{
if (m_portsNames == portsNames)
return;
m_portsNames = portsNames;
emit sig_portsNamesChanged(portsNames);
}
};
#endif // CONNECT_H
dashbboard.cpp
#include <dashboard.h>
#include <QStringList>
#include <QDebug>
DashBoard::DashBoard(QObject *parent)
: QObject(parent)
{
}
void DashBoard::setdashsetup(const QStringList &dashsetup)
{
if (m_dashsetup == dashsetup)
return;
m_dashsetup = dashsetup;
emit dashsetupChanged(dashsetup);
}
//User Dashboard Stringlist
QStringList DashBoard::dashsetup() const { return m_dashsetup; }
dashboard.h
#ifndef DASHBOARD_H
#define DASHBOARD_H
#include <QStringList>
#include <QObject>
class DashBoard : public QObject
{
Q_OBJECT
//User Dashboard Stringlist dashsetup
Q_PROPERTY(QStringList dashsetup READ dashsetup WRITE setdashsetup NOTIFY dashsetupChanged)
public:
DashBoard(QObject *parent = 0);
//User Dashboard Stringlist
QStringList dashsetup() const;
signals:
QStringList m_dashsetup;
};
#endif // DASHBOARD_H
main.qml:
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import com.powertune 1.0
ApplicationWindow {
visible: true
width: 800
height: 480
minimumWidth: 800
minimumHeight: 480
title: qsTr("PowerTune ") + Dashboard.Platform + " Beta 24"
// visibility: "FullScreen"
color: "black"
Connections{
target: Dashboard
ondashsetupChanged: console.log("Dashboard has changed")
}
Item {
id: name
Component.onCompleted: Connect.checkifraspberrypi()
}
SwipeView {
id: view
currentIndex: 0
anchors.fill: parent
Loader {
id: firstPageLoader
source: ""
}
Loader {
id: secondPageLoader
source: ""
}
Loader {
id: thirdPageLoader
source: ""
}
Loader {
id: fourthPageLoader
source: ""
}
Item {
id:lastPage
SerialSettings{}
}
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
The first letter of the property name is always capitalised in signal handlers:
onDashsetupChanged: console.log("Dashboard has changed")
Property Change Signal Handlers explains this:
A signal is automatically emitted when the value of a QML property changes. This type of signal is a property change signal and signal handlers for these signals are written in the form onChanged where is the name of the property, with the first letter capitalized.
So, after I make my QJsonModel, load the data, and set the treeView model, it will load the data normally into the treeView. Although, when I do this after a network request has been finished (serviceRequestFinished(QNetworkReply* reply)), it just doesn't woek. I can qDebug the Json data that is passed, but when it comes down to the Json loading and the model setting. It just fails to finish. Also, for some reason it won't set the Json from the network request, but if I just set it to set the json from a separate button, the json get added successfully. So that means that the Json is formatted correctly. The QJsonModel & QJsonItem are external classes not provided with Qt, that located here.
Here is my code:
Mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QModelIndex>
#include <QItemSelection>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QStringList>
#include <QTimer>
#include <QUrl>
#include <QTreeWidgetItem>
#include <QJsonDocument>
#include <QJsonObject>
#include "qjsonmodel.h"
#include <QClipboard>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
void setJson(QString json);
void getPlaylistList(QString accessToken);
QString disableStreams();
void appendEditValues(int currentRow);
void addVectorItems();
void streamCheck();
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_radioButtonNoStream_clicked()
{
streamCheck();
}
void on_radioButtonTwitchStream_clicked()
{
streamCheck();
}
void on_radioButtonYTStream_clicked()
{
streamCheck();
}
void on_pushButtonAddVid_clicked();
void on_pushButtonApplyAddVid_clicked();
void on_pushButtonDeleteSelection_clicked();
void on_pushButtonApplyAll_clicked();
public slots:
void serviceRequestFinished(QNetworkReply* reply);
void connectAPI(QString code);
signals:
void authenticate(QString accessCode);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "authdialog.h"
#include <qDebug>
#include <QVector>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QFile>
#include <QUrlQuery>
int val = 0;
int selected;
QVector<QString> vidTitles;
QVector<QString> vidUrls;
QVector<int> vidNumber;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
streamCheck();
ui->groupBoxEditVideo->setEnabled(false);
connect(this, SIGNAL(authenticate(QString)), SLOT(connectAPI(QString)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::streamCheck()
{
if (ui->radioButtonNoStream->isChecked())
{
ui->groupBoxStreamSetup->setEnabled(false);
ui->groupBoxStreamSetup->setTitle("No Stream Selected!");
}
else if (ui->radioButtonTwitchStream->isChecked())
{
ui->groupBoxStreamSetup->setEnabled(true);
ui->groupBoxStreamSetup->setTitle("Twitch Setup");
}
else if (ui->radioButtonYTStream->isChecked())
{
ui->groupBoxStreamSetup->setEnabled(true);
ui->groupBoxStreamSetup->setTitle("YouTube Stream Setup");
}
}
void MainWindow::on_pushButtonAddVid_clicked()
{
AuthDialog *auth = new AuthDialog(this);
auth->setModal(true);
auth->exec();
delete auth;
}
void MainWindow::setJson(QString json)
{
qDebug() << json;
QJsonModel * model = new QJsonModel;
model->loadJson(json.toUtf8());
ui->treeView->setModel(model);
delete model;
}
void MainWindow::getPlaylistList(QString accessToken)
{
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
//
QNetworkRequest request(QUrl("https://www.googleapis.com/youtube/v3/search?part=snippet&forMine=true&order=viewCount&type=video&access_token=" + accessToken));
networkManager->get(request);
}
QString MainWindow::disableStreams()
{
QString line = "";
QString test = "";
QFile file;
file.setFileName("C:/WampStack/apache2/htdocs/index.html");
file.open(QIODevice::ReadWrite);
QTextStream in(&file);
while(!in.atEnd())
{
line += in.readAll();
}
if (line.contains("<!--t-->") && !line.contains("<!--y-->"))
{
//Twitch Stream is on, must turn it off
test = line.replace("<!--t-->", "<!--t");
test = test.replace("><!--tt-->", ">tt-->");
return test;
}
else if (line.contains("<!--y-->") && !line.contains("<!--t-->"))
{
//YouTube Stream is on, must turn it off
test = line.replace("<!--y-->", "<!--y");
test = test.replace("><!--yy-->", ">yy-->");
return test;
}
else if (line.contains("<!--y-->") && line.contains("<!--t-->"))
{
test = line.replace("<!--y-->", "<!--y");
test = test.replace("><!--yy-->", ">yy-->");
test = test.replace("<!--t-->", "<!--t");
test = test.replace("><!--tt-->", ">tt-->");
return test;
}
else
{
return line;
}
file.close();
return line;
}
void MainWindow::connectAPI(QString code)
{
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
QUrlQuery postData;
postData.addQueryItem("code", code);
postData.addQueryItem("client_id", "CLIENT-ID");
postData.addQueryItem("client_secret", "CLIENT-SECRET");
postData.addQueryItem("redirect_uri", "REDIRECT-URI");
postData.addQueryItem("grant_type", "authorization_code");
QNetworkRequest request(QUrl("https://accounts.google.com/o/oauth2/token"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
networkManager->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
}
void MainWindow::appendEditValues(int currentRow)
{
}
void MainWindow::addVectorItems()
{
}
void MainWindow::on_pushButtonApplyAddVid_clicked()
{
}
void MainWindow::on_pushButtonDeleteSelection_clicked()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Are you sure?", "Do you want to permanently delete this entry?", QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
}
addVectorItems();
}
void MainWindow::on_pushButtonApplyAll_clicked()
{
QString line = disableStreams();
QFile::remove("C:/WampStack/apache2/htdocs/index.html");
QFile file;
file.setFileName("C:/WampStack/apache2/htdocs/index.html");
file.open(QIODevice::ReadWrite);
//Start code for streaming
if (ui->radioButtonTwitchStream->isChecked() && line.contains("<!--t") && line.contains(">tt-->") && !line.contains("<!--t-->") && !line.contains("<!--tt-->"))
{
QString test = line.replace("<!--t", "<!--t-->");
test = line.replace(">tt-->", "><!--tt-->");
QTextStream stream( &file );
stream << test;
}
else if (ui->radioButtonYTStream->isChecked() && line.contains("<!--y") && line.contains(">yy-->") && !line.contains("<!--y-->") && !line.contains("<!--yy-->"))
{
QString test = line.replace("<!--y", "<!--y-->");
test = test.replace(">yy-->", "><!--yy-->");
QTextStream stream( &file );
stream << test;
}
else if (ui->radioButtonNoStream->isChecked())
{
QTextStream stream(&file);
stream << line;
}
//End code for streaming
file.close();
}
void MainWindow::serviceRequestFinished(QNetworkReply* reply)
{
QByteArray json = reply->readAll();
QString output = QString::fromUtf8(json);
QJsonDocument settdoc = QJsonDocument::fromJson(output.toUtf8());
QJsonObject sett2 = settdoc.object();
if (val == 0)
{
val++;
getPlaylistList(sett2.value(QString("access_token")).toString());
}
else if (val == 1)
{
setJson(output.toUtf8());
}
}
Main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
QJsonModel only works if the the JSON's root element is an object (see the bug report on github.com). I guess your JSON's root is an array.