I am new to the Qt Framework and I have to implement an API call to Login the users. I created an ApiManager class where I want to create only the calls that the app will use, so that they are accessible from C++ classes and also the QML.
I created a local function that simulates a login, but I want to implement the real API call now. I just don't know how to implement it and what kind of libraries I have to import or include.
Header file apimanager.h
#ifndef APIMANAGER_H
#define APIMANAGER_H
#include <QObject>
class ApiManager : public QObject
{
Q_OBJECT
Q_ENUMS(UserLevel)
public:
enum UserLevel {
UL_Unknown = 0,
UL_Master = 1,
UL_Administrator = 2,
UL_Operator = 3
};
explicit ApiManager(QObject *parent = nullptr);
signals:
void loginComplete(bool logged, UserLevel userLevel);
public slots:
void login(QString serverAddress, QString username, QString
password);
};
#endif // APIMANAGER_H
Class apimanager.cpp
#include "apimanager.h"
#include <QDebug>
ApiManager::ApiManager(QObject *parent) : QObject(parent)
{
}
void ApiManager::login(QString serverAddress, QString username,
QString password)
{
qDebug() << "loggin into" << serverAddress << "with user" <<
username;
bool logged = false;
UserLevel ul = UserLevel::UL_Unknown;
if (username=="master" && password=="123") {
logged = true;
ul = UserLevel::UL_Master;
} else if (username=="admin" && password=="123") {
logged = true;
ul = UserLevel::UL_Administrator;
} else if (username=="operator" && password=="123") {
logged = true;
ul = UserLevel::UL_Operator;
}
emit loginComplete(logged, ul);
}
If anyone can tell me how to write an API call with a similar structure of my "fake" login function I'll be thankful.
Thanks to all
#Marco,
Here is the skeletal Http request response example.
It is using Network classes from Qt to send the request and receive the response for the same.
https://github.com/ramkumarrammohan/Qt_HttpNetworkRequest
If you have any doubts about this please let me know, Thanks
Related
Context:
We are developing an IDE for VR/AR effects editing. Users would like to have MACRO recording. Something similar to Selenim/AutoIT in spirit, but specific for this app with deep dataModel integration. Users are trained JS developers. Software GUI is Qt/QML (mostly qml)
Current Blocker
I need a way to uniquely identify a specific QQuickItem by its objectName:
across multiple restart of the app.
across platform (OS/Cpu/...)
name should be human readable enough for use in JS Macro
Should serialise enough information from QQuickItem/QEvent to post a valid event
It is acceptable to use CI static analysis to ensure all item have a valid unique name and detect name changes.
The problem here is some button, for instance the number buttons have exactly the same fqn/path/name unless I use and ugly hack, renaming it by the text of its sub-component:
Component.onCompleted: {
objectName = "button." + button.text
}
property bool keepObjectName: true
Here a fully qualified name based on metaObject()->className() is not enough, as it wont be unique. qml ids are not usable as per documentation.
Question: Is there a way in Qt to obtain such unique identifier?
Related Question:
Qt GUI event recording and playback
Abandoned Road:
print trace of signals/slots called
Reason: Most events in IDE do not use connect() the standard Qt way. Too Low level.
Draft Technical Solution / POC (mostly for reference)
https://doc.qt.io/qt-5/qtdoc-demos-calqlatr-example.html
Using the Qt QML calculator example here. Added this to main.cpp:
#include "Filter.h"
#include <unordered_map>
std::unordered_map<QString,QQuickItem*> map;
void install(QQuickItem* root, KeyPressEater* keyPressEater, const QString& fqn){
if(!root->property("keepObjectName").isValid() || !root->property("keepObjectName").value<bool>()){
qDebug("setObjectName: %s", root->metaObject()->className());
qDebug("Object original name:%s", root->objectName().toStdString().c_str());
root->setObjectName(root->metaObject()->className());
}
else{
qDebug("unchanged Object Name: %s", root->objectName().toStdString().c_str());
}
auto fqnRoot = fqn.isEmpty() ? root->objectName() : fqn + "." + root->objectName();
qDebug("Object fqn: %s", fqnRoot.toStdString().c_str());
root->installEventFilter(keyPressEater);
keyPressEater->insert(root,fqnRoot);
for(auto* child: root->childItems()){
QString fqn = fqnRoot + "." + child->metaObject()->className();
install(child, keyPressEater,fqnRoot);
}
qDebug("\n\n\n");
}
Filter.h looks like this:
#ifndef FILTER_H
#define FILTER_H
#include <QQuickItem>
#include <QtCore>
#include <unordered_map>
#include <QQuickItem>
class KeyPressEater : public QObject
{
Q_OBJECT
public:
void insert(QQuickItem* item,const QString& str){
if(map2.find(str) != map2.end())
{
qDebug("Duplicate object name:%s", str.toStdString().c_str());
}
else{
qDebug("Object name:%s", str.toStdString().c_str());
map.insert({item,str});
map2.insert({str,item});
}
}
void play(){
playing = true;
for(auto p: records){
//p.second->event(p.first);
QCoreApplication::postEvent(p.second, p.first);
QCoreApplication::sendPostedEvents();
}
playing = false;
}
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress) {
//QMouseEvent *keyEvent = static_cast<QMouseEvent *>(event);
qDebug("click");
QQuickItem *item = static_cast<QQuickItem *>(obj);
qDebug("%s",map[item].toStdString().c_str());
qDebug("%s",item->metaObject()->className());
if(!playing){
records.push_back({event->clone(),item});
}
return false;
} else {
// standard event processing
if(event->type() == QEvent::KeyPress){
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == 32){
play();
}
}
return QObject::eventFilter(obj, event);
}
}
std::unordered_map<QQuickItem*,QString> map;
std::unordered_map<QString,QQuickItem*> map2;
std::atomic<bool> playing = false;
std::vector<std::pair<QEvent*,QObject*>> records;
};
#endif // FILTER_H
How to use:
Copy the above the main.cpp and new filter.h file in the calculator app example. Call install in main function:
install(view.rootObject(), &keyPressEater,"");
A user using QWebEngineView in my application fills some form. This form uses post method to submit data to server. How can I get params from user's body request?
I've found such thing as QWebEngineUrlRequestInterceptor, but it works only for urls.
You can use QWebEnginePage::acceptNavigationRequest.
Whenever a form is submitted, you can get the contents of input by using JavaScript and then accept the request to proceed as usual.
Like Anmol Gautam said, you need to reimplement QWebEnginePage::acceptNavigationRequest function and get needed data using JavaScript.
Here is an example how to do it:
mywebpage.h
#include <QWebEnginePage>
class MyWebPage : public QWebEnginePage
{
Q_OBJECT
public:
explicit MyWebPage(QWebEngineProfile *profile = Q_NULLPTR, QObject *parent = Q_NULLPTR);
protected:
bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame);
}
mywebpage.cpp
MyWebPage::MyWebPage(QWebEngineProfile *profile, QObject *parent):QWebEnginePage(profile, parent),
{
//...
}
bool MyWebPage::acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame)
{
if(type == QWebEnginePage::NavigationTypeFormSubmitted)
{
qDebug() << "[FORMS] Submitted" << url.toString();
QString jsform = "function getformsvals()"
"{var result;"
"for(var i = 0; i < document.forms.length; i++){"
"for(var x = 0; x < document.forms[i].length; x++){"
"result += document.forms[i].elements[x].name + \" = \" +document.forms[i].elements[x].value;"
"}}"
"return result;} getformsvals();";
this->runJavaScript(jsform, [](const QVariant &result){ qDebug() << "[FORMS] found: " << result; });
}
return true;
}
use QWebEngineView::setPage to set your WebPage subclass to WebView before you call WebViews load function.
Here is a link for more info about HTML DOM forms Collection
I'm in the process of moving my code from QtWebKit to QtWebEngine. In general, the transition has been fairly smooth, however, I'm stuck on one particular issue. I use a QWebEngineView to display a Google Maps page. Some of the markers placed have have infowindows that pop up "Click Here for More Information" which opens the link in an external browser.
Using QtWebKit, this was fairly easy through the setLinkDelegation policy. However, it seems a little more complex here. I've tried to follow the example but somehow I need to redefine QWebEnginePage within QWebEngineView. Below is what I've come up with so far. Any idea how I can actually connect this all up?
Thanks
#ifndef MYQWEBENGINEVIEW_H
#define MYQWEBENGINEVIEW_H
#include <QWebEngineView>
#include <QDesktopServices>
class MyQWebEnginePage : public QWebEnginePage
{
Q_OBJECT
public:
MyQWebEnginePage(QObject* parent = 0) : QWebEnginePage(parent){}
bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame)
{
qDebug() << "acceptNavigationRequest("<<url << "," << type << "," << isMainFrame<<")";
if (type == QWebEnginePage::NavigationTypeLinkClicked)
{
QDesktopServices::openUrl(url);
return false;
}
return true;
}
};
class MyQWebEngineView : public QWebEngineView
{
Q_OBJECT
public:
MyQWebEngineView(QWidget* parent = 0);
MyQWebEnginePage* page() const;
};
#endif // MYQWEBENGINEVIEW_H
You don't need the second part. Try this:
QWebEngineView *view = new QWebEngineView();
MyQWebEnginePage *page = new MyQWebEnginePage();
view->setPage(page);
In QWebView it was possible to set a QNetworkCookieJar via QNetworkAccessManager.
QNetworkAccessManager *nam = new QNetworkAccessManager();
nam->setCookieJar(cookieJar);
webView->page()->setNetworkAccessManager(nam);
This was working like a charm.
How can I set a QNetworkCookieJar in new QWebEngine class introduced in Qt5.4?
I found a solution, to share Cookies with QWebEngine and QNetworkAccesManager by using QWebEngineCookieStore. Subclass QNetworkCookieJar:
class CookieWebEngine : public QNetworkCookieJar
{
.....
protected:
// Reimplement this functions to work with your _cookies list;
bool insertCookie(const QNetworkCookie &cookie);
bool deleteCookie(const QNetworkCookie &cookie);
bool updateCookie(const QNetworkCookie &cookie);
bool validateCookie(const QNetworkCookie &cookie, const QUrl &url) const;
private:
// Save Chromium Cookies
QWebEngineCookieStore *_store;
// Save available cookies
QList<QNetworkCookie> _cookies;
}
Now, lets implement a function to load/save cookies in a file:
// Load Chromium Cookies
void CookieWebEngine::loadStore() {
// Save cookies profile shared
QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies);
_store = WebEngineProfile::defaultProfile()->cookieStore();
connect(_store, &QWebEngineCookieStore::cookieAdded, this, &CookieWebEngine::handleCookieAdded);
_store->loadAllCookies();
}
// Load/Save cookies in arraylist in a file
void CookieWebEngine::load() {
// load cookies and exceptions
qRegisterMetaTypeStreamOperators<QList<QNetworkCookie> >("QList<QNetworkCookie>");
const QString location = cookiesDirectory() + COOKIES_FILE;
QSettings cookieSettings(location, QSettings::IniFormat);
_cookies = qvariant_cast<QList<QNetworkCookie> >(cookieSettings.value(QLatin1String("cookies")));
setAllCookies(_cookies);
// Now user iterate and add it to chromium
for (auto cookie : _cookies) {
_store->setCookie(cookie);
}
cookieSettings.sync();
}
void CookieWebEngine::save()
{
QString directory = cookiesDirectory();
if (!QFile::exists(directory)) {
QDir dir;
dir.mkpath(directory);
}
const QString location = directory + COOKIES_FILE;
QSettings cookieSettings(location, QSettings::IniFormat);
cookieSettings.setValue(QLatin1String("cookies"), QVariant::fromValue<QList<QNetworkCookie>>(_cookies));
cookieSettings.sync();
}
Now, just connect and handle cookies loaded from the webview:
void CookieWebEngine::handleCookieAdded(const QNetworkCookie &cookie)
{
if (insertCookie(cookie)) {
qDebug() << "Handle cookie " << cookie;
}
}
Its working well for me. Now, i use chromium to sign in. After, i save my session cookie in the customized cookiejar and I use it in my customized QNetworkAccesManager.
I'm making a program that uses lots of timer and, at intervals of 4 seconds, does an online post to a php script.
I'm coding in QtCreator 5.1. I use classes just like the ones below.
The one below just populates a task list, but throughout the course of 8 to 12 hours, the memory that the program takes up just keep rising and rising gradually.
What am I doing wrong while using this class?
I have to be able to keep posting online like I already am, about every 4 to 8 seconds.
Here's a simple class for taking care of one of my processes:
Header file: tasklistprocess.h
#ifndef TASKLISTPROCESS_H
#define TASKLISTPROCESS_H
#include <QThread>
#include <QtCore>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QListWidget>
#include <QTabWidget>
#include "globalhelper.h"
#include "securityinfo.h"
class TaskListProcess : public QThread
{
Q_OBJECT
public:
explicit TaskListProcess(QListWidget *obj_main, QTabWidget *tabs_main, QString user, QObject *parent = 0);
signals:
void upTaskStorage(int key,QHash<QString,QString> item);
private:
GlobalHelper gh;
Securityinfo sci;
QNetworkAccessManager *nam;
QNetworkRequest request;
QByteArray data;
// this is the disposable params for reusage through out the class
QUrlQuery params;
QString post_data;
QString user_name;
QTimer *tasklist_tmr;
bool get_task_list;
QListWidget *obj;
QTabWidget *tabs;
private slots:
void setTaskList();
void replyFinished(QNetworkReply *reply);
void sendPost(QString file_name, QUrlQuery params);
};
#endif // TASKLISTPROCESS_H`
Source file: tasklistprocess.cpp
#include "tasklistprocess.h"
TaskListProcess::TaskListProcess(QListWidget *obj_main, QTabWidget *tabs_main, QString user, QObject *parent) :
QThread(parent)
{
user_name = user;
get_task_list = false;
obj = obj_main;
tabs = tabs_main;
tasklist_tmr = new QTimer(this);
connect(this,SIGNAL(started()),this,SLOT(setTaskList()));
connect(tasklist_tmr,SIGNAL(timeout()),this,SLOT(setTaskList()));
nam = new QNetworkAccessManager(this);
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
request.setRawHeader( "User-Agent" , "Mozilla Firefox" );
// here we connect up the data stream and data reply signals
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
}
void TaskListProcess::setTaskList()
{
qDebug() << "Your task list was set";
bool in = false;
if(!(tasklist_tmr->isActive()))
{
tasklist_tmr->start(10000);
in = true;
}
if(!(get_task_list))
{
params.clear();
params.addQueryItem("user_name", user_name);
params.addQueryItem("logged_in", "1");
sendPost("getTaskList.php",params);
get_task_list = true;
}
else
{
if(post_data.contains("|*|"))
{
//here i retrieve a piece of information from a php script which is stored in a custom string format
// here we clear the list for the new data to be put in
if(obj->count()>0)
{
obj->clear();
}
int key = 0;
foreach(QString inner_task,post_data.split("|*|"))
{
QHash<QString,QString> task_cont;
//qDebug() << " ";
if(inner_task.contains("*,*"))
{
foreach(QString task_val,inner_task.split("*,*"))
{
if(task_val.contains("*=*"))
{
QStringList key_pairs = task_val.split("*=*");
task_cont.insert(key_pairs[0],key_pairs[1]);
if(key_pairs[0] == "tt")
{
QString val_in;
if(key_pairs[1].length()>10)
{
// this sets the title to the shortened version
// if the string length is too long
val_in = key_pairs[1].left(10) + "....";
}
else
{
val_in = key_pairs[1];
}
obj->addItem("Task :" + QString::fromUtf8(key_pairs[1].toStdString().c_str()));
}
}
}
}
//task_storage.insert(key,task_cont);
emit upTaskStorage(key,task_cont);
key ++;
}
}
get_task_list = false;
}
// here we're checking to see if they are looking at the task tab so it doesn't keep changing
// back and forth between the tabs
bool change = true;
if(tabs->currentIndex() != 0)
{
change = false;
}
if(change)
{
tabs->setCurrentIndex(0);
}else if(in)
{
tabs->setCurrentIndex(0);
}
}
void TaskListProcess::replyFinished(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Error in" << reply->url() << ":" << reply->errorString();
return;
}
QString data = reply->readAll().trimmed();
post_data = data;
if(get_task_list)
{
setTaskList();
}
}
void TaskListProcess::sendPost(QString file_name, QUrlQuery params)
{
post_data = "";
QUrl url(sci.getHost() + file_name);
url.setQuery(params);
data.clear();
data.append(params.toString().toUtf8());
request.setUrl(url);
nam->post(request, data);
}
From the Qt docs http://qt-project.org/doc/qt-5.1/qtnetwork/qnetworkaccessmanager.html
Note: After the request has finished, it is the responsibility of the
user to delete the QNetworkReply object at an appropriate time. Do not
directly delete it inside the slot connected to finished(). You can
use the deleteLater() function.
I would suggest calling reply->deleteLater() in your replyFinished() method.
You should call deleteLater() for an QNetworkReply object after use.
Note: After the request has finished, it is the responsibility of the user to delete the QNetworkReply object at an appropriate time. Do not directly delete it inside the slot connected to finished(). You can use the deleteLater() function.
More information here: http://harmattan-dev.nokia.com/docs/library/html/qt4/qnetworkaccessmanager.html
Thank you everyone for the hel.
It was very simply the "deleteLater()" on the reply in the
void replyFinished(QNetworkReply *reply)
{
}
and it should have looked like this
void replyFinished(QNetworkReply *reply)
{
// after all of your processing
reply->deleteLater();
}
it was such a small problem but one that drove me crazy for a long time so i hope this helps