I'm new to qt mobile development and I have a rather dumb question.
How would I check whether a user runs the app for the first time (both Android and iOS)?
EDIT:
The reason I need this check is that I have an intro SwipeView for the first-timers and after it's read once it should always open the main app screen.
I've tried the way #TrebledJ suggested and it seems to work alright, Or is this stupid to do that in main.cpp?
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSettings>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QSettings settings;
QVariant firstRun = settings.value("first-run");
QQmlApplicationEngine engine;
QUrl startingScreen(QStringLiteral("qrc:/main.qml"));
if(!firstRun.isValid())
settings.setValue("first-run", true);
else
startingScreen.setUrl(QStringLiteral("qrc:/start.qml"));
engine.load(startingScreen);
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
Use QSettings to check for a set value.
QSettings settings;
QVariant val = settings.value("first-time");
if (!val.isValid()) {
// ... first run
settings.setValue("first-time", false); // set a value so that the value is valid on the next run
} else {
// ... not first run
}
In QML, there is the Settings QML Type.
import Qt.labs.settings 1.0
Settings {
id: settings
property bool isFirstTime: true
}
Component.onCompleted: {
if (settings.isFirstTime) {
// ... first run
settings.isFirstTime = false;
} else {
// ... not first run
}
}
However, according to documentation:
Note: This type is made available by importing the Qt.labs.settings module. Types in the Qt.labs module are not guaranteed to remain compatible in future versions.
In consideration of the non-guarantee, Felgo/V-Play's API has a Storage QML Type which can also perform the check in QML. (The first example in their documentation implements this.)
Related
I'm trying to implement autofill functionality in a Qt Webengine based app.
My approach is based off Viper browser's implementation: running script on page's loadFinished signal. The script run just looks up all the fields that can be populated and fills them as simple as elem.value = 'value'.
It works with simple example like e.g. my wifi router config page (which uses very simple logic for forms). But it doesn't work with e.g. https://onlyfans.com/ login page.
When I run my script I get JS errors like:
"js: Uncaught TypeError: Cannot set property 'value' of null".
The minimal example demonstrating the issue:
#include <QApplication>
#include <QObject>
#include <QTimer>
#include <QWebEngineView>
#include <QWebEnginePage>
#include <QWebEngineProfile>
#include <QWebEngineScript>
#include <QWebEngineScriptCollection>
const char* URL_ONLYFANS = "https://onlyfans.com/";
const char* URL_ROUTER = "http://192.168.0.1/";
const char* SCRIPT_ROUTER(R"(
document.querySelector('input[type=text]').value = 'admin';
document.querySelector('input[type=password]').value = 'mypass';
)");
const char* SCRIPT_ONLYFANS(R"(
document.querySelector('input[name=email]').value = "random#gmail.com";
document.querySelector('input[name=password]').value = "randompass";
)");
class Observer : public QObject
{
Q_OBJECT
public:
explicit Observer(QWebEnginePage *page): _page(page)
{}
public slots:
void onPageLoad(bool ok)
{
qDebug() << "Page loaded" << ok;
// Running script right away pretty much guarantees JS errors
// No JS errors if delay is >= 1500 ms:
QTimer::singleShot(1500, this, &Observer::runScript);
qDebug() << "Scheduled running script";
}
void runScript()
{
_page->runJavaScript(SCRIPT_ONLYFANS);
qDebug() << "Ran script";
}
private:
QWebEnginePage *_page{nullptr};
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWebEngineView view;
QWebEngineProfile profile;
QWebEnginePage page(&profile);
view.setPage(&page);
view.setUrl(QUrl(URL_ONLYFANS));
view.resize(1024, 750);
Observer observer(view.page());
QObject::connect(view.page(), &QWebEnginePage::loadFinished,
&observer, &Observer::onPageLoad);
/*
{
QWebEngineScript script;
script.setName("autofill");
script.setSourceCode(SCRIPT_ONLYFANS);
// Also tried `DocumentCreation` and `Deferred`, didn't work for Onlyfans
script.setInjectionPoint(QWebEngineScript::DocumentReady);
script.setRunsOnSubFrames(true);
script.setWorldId(QWebEngineScript::ApplicationWorld);
view.page()->scripts().insert(script);
}
*/
view.show();
return app.exec();
}
#include "main.moc"
As can be seen I tried to insert my scripts in two ways: on page's loadFinished signal (both immediately and with a time delay) and by inserting my script into page's scripts collection. I tried all available insertion points: DocumentCreation, DocumentReady and Deferred.
In all cases I get JS error shown above. Except for the case when I delay script execution by about 1500 ms, which is obviously a guess and is not robust.
I do understand that the elements I'm trying to set are probably not yet created, so I'd like some advice on how to properly time the script execution.
My Qt windows application is ready, but when the application opens, I want the login dialog to be opened, how can I do this? I'm new to Qt and C++. It would be great if it was descriptive.
You have many ways to achieve that... QDialog is a nice way. Here is a short sample using QInputDialog.
One solution could be to add this code in your main.cpp file, and to load the mainwindow only if the credentials are ok.
#include "gmainwindow.h"
#include <QApplication>
#include <QInputDialog>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GMainWindow w;
QString login = QInputDialog::getText(NULL, "Login","Name ?",QLineEdit::Normal);
if (login == "USER")
{
w.show();
}
else
{
//display an error message
return a.quit();
}
return a.exec();
}
Of course you may want to put an encrypted password and other things, but the idea will be more or less the same.
I'm trying to take a screen shot during some tests where the application is being ran on an iOS simulator.
The app looks something like this:
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
#ifdef TEST
thingTests = new ThingTests(&engine);
tabletTests->startTestSuite(argv[1]);
#endif
...
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
ThingTests.cpp
TabletTests::TabletTests(QQmlApplicationEngine *engine) : QObject(nullptr), m_testSuiteName(NULL)
{
m_engine = engine;
}
void TabletTests::startTestSuite(char *testSuiteName)
{
m_testSuiteName = testSuiteName;
connect(m_engine, SIGNAL(objectCreated(QObject*,QUrl)), this, SLOT(onObjectCreated(QObject*,QUrl)));
}
void TabletTests::onObjectCreated(QObject *, const QUrl &) {
// run settings test for now, later on control with command line arguments
SettingsTest *settingsTest = new SettingsTest(m_engine);
QTest::qExec(settingsTest);
}
SettingsTest.cpp
void SettingsTest::openWarningModeSelectionTest()
{
m_settingsTester->performTestOperation(SettingsTester::SettingsTestOperation::WarningModeSelection);
QTest::qWait(1000);
bool optionSelectorDisplayed = DialogController::getInstance()->optionSelectorShown();
// This clip came from here: https://stackoverflow.com/questions/21697185/how-to-take-screenshot-of-qml-application-without-qquickview
foreach(QObject* obj, this->m_engine->rootObjects()) {
QQuickWindow* window = qobject_cast<QQuickWindow*>(obj);
if (window) {
QImage image = window->grabWindow(); //<-- This line throws the assertion error
}
}
QVERIFY(optionSelectorDisplayed);
}
I've inherited this codebase and I'm not very familiar with QT, but I tried to only include relevant things in the above snippets.
What I want to be able to do is take some screenshots on what a page looks like at certain times in the tests.
When I have TEST defined, my tests for the app go and do their thing, but they blowup when the window->grabWindow() line is hit with an ASSERT error in assert w in scenegraph/qsgthreadedrenderloop.cpp in the qt libraries. This is the assert that is failing (https://code.woboq.org/qt5/qtdeclarative/src/quick/scenegraph/qsgthreadedrenderloop.cpp.html#1281)
I have a class that composes a palette and assigns it to the application using QApplication::instance()->setPalette(QPalette palette).
And it effectively works.
But then I try to use QPalette QApplication::instance()->palette() to extract some colours.
But here it does not work, it just returns the default palette, not the current one.
After I have discovered that it is working as supposed and described in the documentation.
And now I have just 2 questions:
Why it is working in such a strange, useless and counter-intuitive
mode?
How I can retrieve the palette which was set using
QApplication::instance()->setPalette(QPalette palette)?
P.S. No, I can't keep that palette elsewhere.
I think it is an issue of your Qt version (you marked the question as Qt 5 but didn't indicate a specific version), or you have something else in your project that is resetting the palette (you mentioned it has a large code base).
This minimum example shows correct behavior, at least with Qt 5.12.3 32bits, Windows, VS 2017:
#include <QApplication>
#include <QPalette>
#include <QDebug>
#include <QTimer>
#include <QWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
const auto group = QPalette::Active;
const auto role = QPalette::Text;
auto palette = QApplication::palette();
qDebug() << "palette before:" << palette.color(group, role).name();
palette.setColor(group, role, "#123456");
qDebug() << "palette set:" << palette.color(group, role).name();
QApplication::setPalette(palette);
const auto palette2 = QApplication::palette();
qDebug() << "palette after:" << palette2.color(group, role).name();
QTimer::singleShot(100, [=]() { // check palette after the events loop has started
const auto palette3 = QApplication::palette();
qDebug() << "palette after 100ms:" << palette3.color(group, role).name();
});
QWidget w;
w.show();
return a.exec();
}
I've used QApplication::palette my self to retrieve custom palettes in different projects and had no issues at all.
QGuiApplication::setPalette is documented to change the default palette, so basically I think default palette means the palette used if a widget doesn't specify the other one; not the default system palette.
PS: I couldn't make it compile when using QApplication::instance()->setPalette since QApplication doesn't defines instance() but it falls to QCoreApplication::instance(), which obviously returns a QCoreApplication. Probably just a typo when you wrote the question, but I thought it deserved some lines. Given that the palette related methods are static, I decided to use those in the example, but I had the same results using the singleton from qApp.
I have the following Qt code:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
ChoosingDialog cdlg;
if(!startWin.exec())
{
// nothing chosen
return 0;
}
if(cdlg.firstWindowSelected)
{
CFirstWindow win;
win.show();
return app.exec();
}
else
{
CSecondWindow win;
win.show();
return app.exec();
}
}
this seems to work but it's giving me an error on "event dispatcher cleanup" in some asm line. I tried to trick a bit and I saw that the problem is related to the app.exec() calling.
Update:
if I add these lines to the ChoosingDialog (this is a simple blank class auto-generated by Qt Creator)
void ChoosingDialog ::closeEvent(QCloseEvent *)
{
exit(1);
}
I receive no errors
Turns out I was using the "singleapplication" class before the QApplication.. and something went wrong on the shared memory lock.
As soon as I restarted my system the exception disappeared... bof.. do you believe in magic?