how to take a screenshot Within a QT QTest setting - c++

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)

Related

Trouble with QT timers: "function definition is not allowed here"

I am trying to make a program that takes images and puts them on your wallpaper using a timer, but I kept getting the error "Timers can only be started with QThread", so I am trying to make this timer with more QThread elements (simpler designs like QThread::msleep haven't worked). Currently, my problem is that my calling slot for when the timer goes off is not working where it currently is, but if I put it in any other location, then the program spits out more errors as it is designed to go in that specific spot. The code itself is mainly a copy/paste of a bunch of other code, and I am new to QT, so I may be going about this completely wrong. If I am, I will gladly accept help so I can understand this better!
#include <mainwindow.h>
#include <mythread.h>
QMediaPlayer * BadAppleS = new QMediaPlayer();
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
int fileN = 0;
BadAppleS->setMedia(QUrl("qrc:/SongN/Bad Apple.mp3"));
BadAppleS->play();
mythread t;
t.start();
if (fileN <= 1625) {
void mythread::doIt(){ //Error here. No more errors elsewhere, though there may be in this function/signal.
QString fileNQ = QString::number(fileN);
QString filepath = (("qrc:/BAPics/scene (") + fileNQ + (")"));
char path[150];
wchar_t wtext[20];
strcpy_s(path, filepath.toStdString().c_str());
mbstowcs(wtext, path, strlen(path)+1);
LPWSTR pathp = wtext;
int result;
result = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, pathp, SPIF_UPDATEINIFILE);
fileN++;
}
return app.exec();
}
}
Thank you for the help!

Check that the app runs for the first time

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.)

Why QFileDialog::selectFile doesn't work?

I am using Qt 5.12 and trying to write test (using QTest) for opening of project stored in some xml format.
In test I use QTimer::singleShot to wait QFileDialog to appear as proposed in QT close window by QTest in locked thread.
The problem is that QFileDialog::selectFile doesn't select anything. This means that OK button is not active, so I can't click on it in my test.
I assume in the following example that full path to file is /tmp/project.xml. Notice that QFileDialog::setDirectory works great: when the following example starts, you are in /tmp dir instead of /.
#include <QApplication>
#include <QFileDialog>
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTimer::singleShot(300, []() {
QWidget* window = nullptr;
while (!window) {
window = qApp->activeModalWidget();
}
QFileDialog* fd = qobject_cast<QFileDialog*>(window);
fd->setDirectory("/tmp");
fd->selectFile("project.xml");
});
QString path = QFileDialog::getOpenFileName(nullptr, "Open Project",
"/", QString(),
nullptr, QFileDialog::DontUseNativeDialog);
}
The function selectFile didn't work because of the file name text box (lineEdit) is focused. This behavior comes from the implementation of QFileDialog::selectFile() :
void QFileDialog::selectFile(const QString &filename)
{
// ... function body
//Put the filename into the lineEdit when :
//1. The dialog is not visible or
//2. The lineEdit is not focused.
if (!isVisible() || !d->lineEdit()->hasFocus())
d->lineEdit()->setText(index.isValid() ? index.data().toString() : fileFromPath(d->rootPath(), filename));
}
To make the program works, there are two ways :
Put the file name directly in the text box
Give the focus away then call selectFile
fd->setDirectory("/tmp");
QLineEdit * lineEdit = qobject_cast<QLineEdit*>(fd->focusWidget());
if( lineEdit ){
//Method 1
lineEdit->setText("project.xml");
//Method 2
lineEdit->nextInFocusChain()->setFocus();
fd->selectFile("project.xml");
}else { //no lineEdit focus found
fd->selectFile("project.xml");
}

Qt - Dialog in a DLL

In my company, we are developing with Embarcadero-C++-IDE (which is very uncomfortable). To start moving away, we port individual dialogs in a dll to Qt. My qt-dll-code Looks like this for example
extern "C" ROBOTECHPOLYLINEDIALOGSHARED_EXPORT void popupRoboTechDialog()
{
if( ! QApplication::instance() )
{
int argc = 1;
char *argv[] = {"Design polyline"};
QApplication app(argc, argv);
RoboTechPolyline dialog;
dialog.show();
app.exec();
}
else
{
RoboTechPolyline Dialog;
Dialog.exec();
}
}
Trying to start the Dialog from another thread like here Starting Qt GUI from dll (in DLLStart function) did make my Dialog unresponsive, but I don't think the question and mine relate too much.
I'm loading this Dll dynamically from the main-application and it works fine. However, when I make the Dialog Pop up a second time I get an "Access Violation at address .. in module MSVCR110D.dll" and on the third time, I get "ASSERT failure in QCoreApplication , there should be only one application object". So I always Need to Close the whole application in order to make the Dialog appear a second time, which greaty slows down work.
If I add at the bottom the line
QApplication::quit()
the Dialog appears a second time, but the Programm crashes on closing this second Dialog.
The code to load the dll is as follows
HINSTANCE lib = ::LoadLibrary(L"RoboTechPolylineDialog.dll");
if(!lib)
{
ShowMessage("Unable to load RoboTechPolylineDialog.dll");
return;
}
typedef void ( *POPUP_ROBO_TECH_DIALOG )();
POPUP_ROBO_TECH_DIALOG fp = (POPUP_ROBO_TECH_DIALOG) ::GetProcAddress(lib, "popupRoboTechDialog");
if(!fp)
{
ShowMessage("Unable to load function popupRoboTechDialog from RoboTechPolylineDialog.dll");
::FreeLibrary(lib);
return;
}
(*fp)( );
FreeLibrary(lib);
So why am I constructing more than one QApplication at a time? I can in above code replace the line
(*fp)();
with
(*fp)();
(*fp)();
and the Dialog appears twice and everything works greatly. But how can the call to ::FreeLibrary(lib) make things fail.
Can anyone help me? Any help, Workarounds, etc.. is appreciated.
This should work:
#include <QApplication>
#include <QString>
#include <QDialog>
class App {
QApplication *_app;
public:
App(int argc = 0, char** argv = NULL)
: _app(new QApplication(argc, argv))
{
}
~App() {
delete _app;
}
};
void dialog()
{
static int argc = 1;
static char *argv[] = {"Design polyline"};
static App(argc, argv);
QDialog dlg;
dlg.exec();
}
void main()
{
dialog();
dialog();
dialog();
}
Another advice: load Qt libs from as subpath since you could find dll conflict with other apps using it on the same folder (personal experience)

Qt - confused on QDialog choice on main

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?