Displaying qml from c++ differs from what qmlscene shows - c++

I'm try to run a basic program that displays a simple qml file through c++. The code for loading QQmlEngine etc looks like this:
#include <QQmlEngine>
#include <QQmlComponent>
#include <QDebug>
#include <QGuiApplication>
#include <QQuickView>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlEngine engine;
QQmlComponent component(&engine);
QQuickWindow::setDefaultAlphaBuffer(true);
component.loadUrl(QUrl("qrc:///qmlFiles/main.qml"));
if ( component.isReady() )
component.create();
else
qWarning() << component.errorString();
QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(),SLOT(quit()));
return app.exec();
}
and the qml (simplified) file is:
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.0
ApplicationWindow
{
width:800
height:600
//background: "white"
visible:true
function selectFile()
{
fileChooser.visible=true;
}
menuBar:MenuBar {
Menu{
title:"File"
MenuItem{
text:"Choose File"
shortcut: "Ctrl+F"
onTriggered:{
selectFile();
}
}
MenuItem {
text:"Quit"
shortcut: "Ctrl+Q"
onTriggered: Qt.quit()
}
}
}
FileDialog{
id:fileChooser
visible:false
modality:Qt.WindowModal
title:"Choose data file"
onAccepted:{
console.log(fileChooser.fileUrls)
visible:false
}
onRejected:{
console.log("Cancel")
visible:false
}
}
}
When I run the file from terminal using qmlscene the displayed looks different than while running it from the c++ program.
My guess is that the C++ implementation is unable to use platform specific stuff (for example QFileDialog) and falls back to qml implementation of the stuff.
I guess I need to load the qml file differently, but how?

Turns out there is some overriding or something with QGuiApplication .
Changed it to QApplication and looks as nice as in qmlscene.

Related

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

running draw_polygon example in CGAl package in qt widget application

I try to following CGAL example in Qt widget application :
example
main.ccp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.ccp :
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/draw_polyhedron.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
void MainWindow::on_pushButton_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this,tr("Open .off model"), "/home", tr("*.off"));
draw_poly(fileName);
}
void MainWindow::draw_poly(QString fileName)
{
QByteArray inBytes;
const char *c;
inBytes = fileName.toUtf8();
c = inBytes.constData();
std::ifstream input(c);
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << "Not a valid off file." << std::endl;
// return 1;
}
input >> mesh;
CGAL::draw(mesh);
}
when I ran it , it open dialog file to select .off file ,then it shows the following error:
QCoreApplication::exec: The event loop is already running
any help ,please ?
I'm using Qt5 in daily business, and once considered CGAL as possible application base (without going further into this direction – not yet). Hence, this question made me curious.
I digged through the source code of CGAL on github and found out why the error message
QCoreApplication::exec: The event loop is already running
occurs.
For this, I copied the relevant lines from CGAL on github: Polyhedron/include/CGAL/draw_polyhedron.h:
template<class Polyhedron, class ColorFunctor>
void draw(const Polyhedron& apoly,
const char* title,
bool nofill,
const ColorFunctor& fcolor)
{
#if defined(CGAL_TEST_SUITE)
bool cgal_test_suite=true;
#else
bool cgal_test_suite=false;
#endif
if (!cgal_test_suite)
{
int argc=1;
const char* argv[2]={"polyhedron_viewer","\0"};
QApplication app(argc,const_cast<char**>(argv));
SimplePolyhedronViewerQt<Polyhedron, ColorFunctor>
mainwindow(app.activeWindow(), apoly, title, nofill, fcolor);
mainwindow.show();
app.exec();
}
}
Looking at this source code, it becomes obvious that CGAL::draw() is a small ful-featured Qt application in itself which establishs its own QApplication instance. The OP in turn tried to embed the CGAL::draw() in her/his own Qt application. It is not allowed to instance any derivates of QCoreApplication more than once (according to Qt doc. of QApplication):
For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time.
(Emphasizing not mine.)
The CGAL doc. provides an (even shorter) example in Polyhedron/draw_polyhedron.cpp to do this right:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/draw_polyhedron.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
int main(int argc, char* argv[])
{
Polyhedron P;
std::ifstream in1((argc>1)?argv[1]:"data/cross.off");
in1 >> P;
CGAL::draw(P);
return EXIT_SUCCESS;
}
but there is no place to insert the QFileDialog at the right point.
Hence, CGAL::draw() is the wrong tool for what OP (probably) intends to do – embed CGAL polyhedron rendering into a Qt application. For this, it is necessary to use the things directly which are called somewhere inside of CGAL::draw().
So, this is what seems appropriate to me:
making SimplePolyhedronViewerQt<Polyhedron, ColorFunctor> a (main or child) widget in OPs Qt application.
I then walked a bit through the github repo to find out from which Qt widget CGAL::SimplePolyhedronViewerQt<Polyhedron, ColorFunctor> is actually derived from and found the following inheritance:
CGAL::SimplePolyhedronViewerQt<Polyhedron, ColorFunctor>
|
V
CGAL::Basic_viewer_qt
|
V
CGAL::QGLViewer
|
+--------------+--------------+
| |
V V
QOpenGLWidget QOpenGLFunctions
So, CGAL::SimplePolyhedronViewerQt<Polyhedron, ColorFunctor> can be used like any QWidget (which involves making it the main window). It can become as well the center widget of a QMainWindow which gets a menu bar/tool bar with the QAction to open the QFileDialog, request a file path, open a file stream with this file path, and load a mesh from this file stream.
There is another minor detail where I stumbled over: The CGAL::Polyhedron has to be given to the CGAL::SimplePolyhedronViewerQt in the constructor and by const reference. To consider this, it's IMHO necessary (after successful loading of mesh) to construct the CGAL::SimplePolyhedronViewerQt instance by new and set/add it to parent widget afterwards. If this is not acceptable it's probably necessary to go even deeper and replace the CGAL::SimplePolyhedronViewerQt by an own implementation, using the source code of the former as “cheat-sheet”.
This is how such an application could look like:
#include <fstream>
#include <QtWidgets>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/draw_polyhedron.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
CGAL::DefaultColorFunctorPolyhedron fColor;
Polyhedron mesh;
// setup UI
QMainWindow qWin;
QToolBar qToolbar;
QAction qCmdLoad(QString::fromUtf8("Load File..."));
qToolbar.addAction(&qCmdLoad);
qWin.addToolBar(&qToolbar);
qWin.show();
// install signal handlers
QObject::connect(&qCmdLoad, &QAction::triggered,
[&qWin, &mesh, &fColor]() {
const QString filePath = QFileDialog::getOpenFileName(
&qWin,
QString::fromUtf8("Open .off model"),
QString::fromUtf8("/home"),
QString::fromUtf8("*.off"));
if (filePath.isEmpty()) return;
std::ifstream fIn(filePath.toUtf8().data());
if (!(fIn >> mesh) || mesh.is_empty()) {
qDebug() << "Loading of" << filePath << "failed!";
return;
}
qWin.setCentralWidget(
new CGAL::SimplePolyhedronViewerQt<Polyhedron, CGAL::DefaultColorFunctorPolyhedron>(
&qWin, mesh, "Basic Polyhedron Viewer", false, fColor));
qWin.centralWidget()->show();
});
// runtime loop
return app.exec();
}
Please, take this with a “grain of salt” – I've no CGAL at hand and couldn't compile/test the above code.
CGAL::draw() already handles the Qt stuff. You are trying to open a mainwindow in another one. Just call CGAL::draw(mesh) in your main() function without anything else and it will work.
EDIT: Which is exactly what Sheff explained in a much more detailed way.

QCompleter::activated disconnected after losing and gaining focus

I found a strange case which I do not understand. Maybe it is a Qt bug, maybe I am doing something wrong.
A header:
// File mylineedit.h
#pragma once
#include <QLineEdit>
#include <QDebug>
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit MyLineEdit(QWidget *parent = nullptr) : QLineEdit(parent) { }
public slots:
void onCompleterActivated(const QString& text) { qDebug() << "MyLineEdit" << text; }
};
And the main source file:
// File main.cpp
#include <QApplication>
#include <QWidget>
#include <QStringListModel>
#include <QCompleter>
#include <QVBoxLayout>
#include "mylineedit.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QLineEdit* lineEdit1 = new MyLineEdit();
QLineEdit* lineEdit2 = new MyLineEdit();
auto layout = new QVBoxLayout(&w);
layout->addWidget(lineEdit1);
layout->addWidget(lineEdit2);
lineEdit1->setCompleter(new QCompleter());
auto model = new QStringListModel(QStringList() << "A" << "B" << "C");
lineEdit1->completer()->setModel(model);
QObject::connect(lineEdit1->completer(), SIGNAL(activated(QString)), lineEdit1, SLOT(onCompleterActivated(QString)));
w.show();
return a.exec();
}
Once you run this, you can get a completer with values "A", "B", "C" in the first line edit. When you select any of these values, it will print the message to the console. This is correct. But then change focus to the other line edit and then back. Try picking "A", "B", "C" again. No message is printed out, the signal/slot seems disconnected. Any ideas?
Tested with MinGW 5.3.0 and MSVC 2015 using with Qt 5.9.2.
I modified your sample slightly and tried to reproduce the behavior you described but I couldn't:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
QVBoxLayout qVBox;
QLineEdit qEdit1;
qVBox.addWidget(&qEdit1);
QCompleter qCompl1;
QStringListModel qComplModel(QStringList() << "A" << "B" << "C");
qCompl1.setModel(&qComplModel);
qEdit1.setCompleter(&qCompl1);
QLineEdit qEdit2;
qVBox.addWidget(&qEdit2);
qWin.setLayout(&qVBox);
qWin.show();
// install signal handlers
QObject::connect(&qCompl1,
static_cast<void(QCompleter::*)(const QString&)>(
&QCompleter::activated),
[](const QString &text)
{
qDebug() << "Activated: " << text;
});
// run-time loop
return app.exec();
}
The significant differences in my sample:
I used Qt5 style signal handlers (as I'm used to).
I didn't new the Qt widgets (as I'm used to when I write minimal samples).
For me, it's hard to believe that one of these changes makes your described issue unbroken.
I compiled and tested with VS2013, Qt 5.9.2 on Windows 10 (64 bit):
I did the following interactions:
click in qEdit1
type A and click on item A in the popup menu
Output:
Activated: "A"
I continued with:
click in qEdit2
type B
click in qEdit1
erase text, type B, and click on item B in the popup menu
Output:
Activated: "B"
It works like expected.
After some conversation, I had a look at woboq.org in the source code of QLineEdit:
The interesting part is in QLineEdit::focusOutEvent:
if (d->control->completer()) {
QObject::disconnect(d->control->completer(), 0, this, 0);
}
If I read this right, all signal handlers of QLineEdit (emitted by the set completer) are disconnected. I believe this is the reason for the observed issue. (Therefore, it works for lambdas and methods of other classes.)

Qt 5 QPrinterInfo::availablePrinters() not listing printers dynamically

I am updating the printer list using availablePrinters(). But it fails to list the new printer added while running application. It is working fine with Qt 4.
The code can be seen below:
#include <QCoreApplication>
#include <QtPrintSupport/QPrinterInfo>
#include <QThread>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
while (1) {
QThread::msleep(3000);
qDebug()<<"List of printers";
QList<QPrinterInfo> printerList=QPrinterInfo::availablePrinters();
foreach (QPrinterInfo printerInfo, printerList) {
qDebug()<<printerInfo.printerName();
}
}
return a.exec();
}
That was a bug with the existing Qt version, and It got fixed on the next version

ReferenceError when using c++ and qml on Nokia N9

I want to integrate c++ and qml. However, my code works fine in simulator but not in Nokia N9 (Qt 4.7.4 harmattan_10.2011.34-1)
Here is my code
I pasted c++ and qml code here for your reference
#include <QtGui/QApplication>
#include <QtDeclarative/QDeclarativeView>
#include <QtDeclarative/QDeclarativeContext>
#include <QtDeclarative/QDeclarativeEngine>
#include "qmlapplicationviewer.h"
#include "data.h"
#include "testfactory.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
TestFactory *testfactory = new TestFactory();
QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create());
QDeclarativeContext *context = viewer->rootContext();
context->setContextProperty("testfactory", testfactory);
viewer->setOrientation(QmlApplicationViewer::ScreenOrientationLockPortrait);
viewer->setMainQmlFile(QLatin1String("qml/main.qml"));
viewer->showExpanded();
testfactory->intilize();
return app->exec();
}
Button {
id: startButton
text: qsTr("Start")
onClicked: {
mainview.state = "START"
testfactory.startMeasurement()
}
}
The wield part is that the code works on simulator but the device.
The error I get is ReferenceError: Can't find variable: testfactory
Any one knows what the reason is?
Based on comments from irc qt-qml, one solution is to just use
QmlApplicationViewer *viewer = new QmlApplicationViewer();
instead of
QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create());
Then code works.