QML Splash Screen Not Working - qt

My app used to work before the latest belle update but doesnt anymore. The splash screen only works when i downgrade com.nokia.symbian 1.1 to 1.0 on both the init.qml file and the splashcreen.qml file but then doesnt display the main.qml file. When I instruct main.cpp to directly load main.qml the app does work…. Im lost! Here is the code I have for main.cpp:
#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockPortrait);
viewer.setSource(QUrl("qrc:/qml/SmartFlyer/init.qml"));
//viewer.setMainQmlFile(QLatin1String("qrc:qml/SmartFlyer/main.qml"));
viewer.showExpanded();
return app->exec();
}
For init.qml:
import QtQuick 1.1
import com.nokia.symbian 1.1
Item {
id: init
SplashScreen {
id: splash
show: true // show splash
minTimeout: 3000 // show splash at least for 3 sec
image: "data/splash_screen.png" // path to splash image
canFinish: false // change to true when main QML will be loaded
z: 100 // highest page.
}
Loader { // this component performs deferred loading.
id: mainLoader
onStatusChanged: {
if( mainLoader.status == Loader.Ready )
{
// main page is loaded
// time to hide splash
splash.canFinish = true
}
}
}
Component.onCompleted: {
// splash is already rendered on the screen
// user is looking on splash
// now we can start loader to load main page
mainLoader.source = "main.qml"
}
}
And for splashscreen.qml :
import QtQuick 1.1
import com.nokia.symbian 1.1
Rectangle {
id: splash
anchors.fill: parent
color: "black"
property int minTimeout: 3000 // 3s by default.
property string image; // path to splash image
property bool show: false // if show is true then image opacity is 1.0, else 0.0
property bool canFinish: false // if true then we can hide spash after timeout
state: show ? "showingSplash" : ""
onStateChanged: {
if( state == "showingSplash" )
splashTimer.start();
}
opacity: 0.0
Image {
source: image
fillMode: Image.PreserveAspectFit
anchors.fill: parent
smooth: true
}
Timer {
id: splashTimer
interval: minTimeout
running: false
repeat: true
onTriggered: {
if( splash.canFinish )
{
// finally we can stop timer and hide splash
splash.show = false
splashTimer.repeat = false
}
else
{
// canFinish is false, but main.qml is not loaded yet
// we should run timer again and again
splashTimer.interval = 1000 // 1 sec
splashTimer.repeat = true
}
}
}
states: [
State {
name: "showingSplash"
PropertyChanges { target: splash; opacity: 1.0 }
}
]
// hide splash using animation
transitions: [
Transition {
from: ""; to: "showingSplash"
reversible: true
PropertyAnimation { property: "opacity"; duration: 500; }
}
]
}

As for me, I use an "old school" way to show Splash without any QML. Here you can see, how to construct it, with image logo placed on the gradiented background:
QSplashScreen *Application::buildSplashScreen() {
const QPixmap logoPixmap(":/images/logo.png");
QDesktopWidget *desktop = QApplication::desktop();
QRect desktopRect = desktop->availableGeometry();
QPixmap splashPixmap(desktopRect.width(), desktopRect.height());
QPainter painter;
painter.begin(&splashPixmap);
QLinearGradient backgroundGradient(splashPixmap.rect().width() / 2,
0,
splashPixmap.rect().width() / 2,
splashPixmap.rect().height());
backgroundGradient.setColorAt(0, QColor::fromRgb(40, 50, 57));
backgroundGradient.setColorAt(1, QColor::fromRgb(19, 25, 29));
painter.fillRect(splashPixmap.rect(), backgroundGradient);
QRect logoRect((splashPixmap.width() - logoPixmap.width()) / 2,
(splashPixmap.height() - logoPixmap.height()) / 2,
logoPixmap.width(),
logoPixmap.height());
painter.drawPixmap(logoRect, logoPixmap);
painter.end();
QScopedPointer<QSplashScreen> splashScreen(new QSplashScreen(splashPixmap));
if (desktopRect.width() > desktopRect.height()) {
splashScreen->setAttribute(Qt::WA_LockLandscapeOrientation, true);
} else {
splashScreen->setAttribute(Qt::WA_LockPortraitOrientation, true);
}
return splashScreen.take();
}
Then I use this manually built splash when program starts up:
int Application::run() {
QScopedPointer<QSplashScreen> splashScreen(buildSplashScreen());
splashScreen->showFullScreen();
QScopedPointer<QDeclarativeView> applicationWindow(buildRootView());
splashScreen->finish(applicationWindow.data());
applicationWindow->showFullScreen();
return QApplication::exec();
}

Related

QML Virtual Keyboard not deactivated when I press "Hide" button

I've created a Virtual Keyboard control on a QML screen. However, while I can show and hide the keyboard in my code fine, when I press the "Dismiss" button, it doesn't hide.
I don't think this is a duplicate of QML Virtual keyboard Hide button not working because that question seems to be about the visibility not being bound to the active status whereas in my case, the active status is not being updated at all from what I can tell, and my setup is different. Also I'm not getting any error message while that poster did.
Here is my Keyboard input panel (Keyboard.qml):
import QtQuick 2.0
import QtQuick.VirtualKeyboard 2.1
InputPanel {
id: keyboard
z: 99
y: parent.height
anchors.left: parent.left
anchors.right: parent.right
onActiveChanged: {
console.info("keyboard active changed"); // this doesn't get hit when I press the hide button
}
states: State {
name: "visible"
when: keyboard.active
PropertyChanges {
target: keyboard
y: parent.height - keyboard.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
Binding {
target: InputContext
property: "animating"
value: inputPanelTransition.running
}
}
Then on my QML screen I have this:
TextInput {
id: myField
anchors.fill: parent
anchors.margins: vitalStyle.marginSmall
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignBottom
clip: true
onTextChanged: {
if (model.fieldType === SettingsManager.WLAN_PASSKEY) {
resultData = text;
dataUpdated++;
}
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onPressed:{
keyboard.active = true; // works fine - makes keyboard pop up.
mouse.accepted = false;
if( (otherScreen.visible) ) {
keyboard.active = false; // works fine - hides keyboard if otherScreen is visible.
}
}
onActiveFocusChanged: {
console.info("active focus changed (basic)"); // doesn't get hit if I press the dismiss button.
if( (focus) ) {
cursorPosition = text.length;
clearInputMethod();
console.info("activating keyboard"); // doesn't get hit if I press the dismiss button.
keyboard.active = true;
}
else{
clearInputFocus.enable();
}
EnterKeyAction.enabled: false
}
}
.
.
.
.
function clearInputMethod() {
// sets 'visible' to false of a number of other controls.
keyboard.active = false;
}
TextInput {
id: clearInputFocus
readOnly: true
function enable() {
forceActiveFocus();
clearInputMethod();
}
}
Keyboard {
id: keyboard
}
I was afraid that pressing the 'Hide' button might cause the focus to go back to the text field and then maybe it was just popping it back up immeadiately because of the code in onActiveFocusChanged however those console.log statements aren't displaying anything when I press the Hide button so I don't think that's the case. Neither is the onActiveChanged event on the InputPanel itself being hit when I press the button. Though both are being hit when I first show the keyboard by clicking on the field.
The button itself does dim slightly as I press it, as all of the buttons do, so the button press is "occurring" if you get me.
I ended up having to use an "eventFilter" which detects when the region that the Hide button occupies is pressed. Not very elegant I know...
bool contentLoader::eventFilter( QObject *object, QEvent *event )
{
if( (m_keyboardActive) &&
(event->type() == QEvent::TouchEnd) )
{
QTouchEvent *touch = static_cast<QTouchEvent *>(event);
if( (touch->touchPoints().count()) )
{
QTouchEvent::TouchPoint lastPoint = touch->touchPoints().last();
//Normalised location of the Hide Keyboard Button
QRectF hideButton_normalised( 0.8725, 0.90, 1.0, 1.0 ); //fullsize keyboard
if( ((m_inputHint & Qt::ImhDigitsOnly) == Qt::ImhDigitsOnly) )
{
hideButton_normalised.setCoords( 0.55, 0.85, 0.87, 0.90 ); //numpad
}
if( (hideButton_normalised.contains( lastPoint.normalizedPos() )) )
{
hideKeyboard();
}
}
}
and in the QML...
Connections {
target: contentHandler
onHideKeyboard: {
keyboard.active = false;
}
}

QML Loading View during function runtime

I am attempting to create a qml button object that displays a screen for the duration of a function's runtime. I plan to use this loading screen when I need to parse through a larger dataset/run a slower function. Currently this is what I have come up with.
//LoadingButton.qml
import QtQuick 2.4
import QtQuick.Controls 1.2
Item
{
id: impl
function callbackFunction() { console.log("This is a dummy funciton and needs to be overwritten in the implementation") } //empty dummy function
property alias style: button.style
Button {
id: button
anchors.fill: parent
onClicked: {
loadingScreen.visible = true;
console.log("Loading should be visible")
impl.callbackFunction();
loadingScreen.visible = false;
console.log("Loading should be hidden")
}
}
Rectangle
{
width: 500
height: 500
x:0
y:0
z: 60
id: loadingScreen
color: "red"
visible: false
}
}
This example runs the callbackFunction once overwritten in the parent object correctly, but the visibility of the Rectangle does not change until the slower function is completed. Also the application freezes until it finishes.
Is there any way to force the Rectangle to show/hide mid-javascript function execution?
the best solution is of course to move your slow function to a background thread. That way the GUI stays responsive.
If you want to keep the callbackFunction in same thread as the GUI, you can use a Timer that will delay the start of the slow function until the loading screen is shown. Please note that the GUI will be blocked during the execution of the slow function.
import QtQuick 2.4
import QtQuick.Controls 1.2
Item
{
id: impl
function callbackFunction() {
console.log("This is a dummy funciton and needs to be overwritten in the implementation")
var cnt = 0
var largeNumber = 1
while (cnt < 99999999) {
largeNumber += largeNumber/3
cnt++
}
//put this at the end of your slow function
loadingScreen.visible = false;
console.log("Loading should be hidden")
}
property alias style: button.style
Button {
id: button
anchors.fill: parent
onClicked: {
loadingScreen.visible = true;
console.log("Loading should be visible")
timer.start()
}
}
Timer {
id: timer
interval: 500
repeat: false
onTriggered: impl.callbackFunction()
}
Rectangle
{
id: loadingScreen
width: 500
height: 500
x:0
y:0
z: 60
color: "red"
visible: false
BusyIndicator {
anchors.centerIn: parent
running: loadingScreen.visible
}
}
}

Trigger QQuickImageProvider::requestImage() with parameter via button click

I have a QQuickControls 2 application that displays an image using a QQuickImageProvider. Sometimes I will simply want to show an unedited image so I just implement my QML like so:
Image {
id: image
fillMode: Image.PreserveAspectFit
source: "image://provider/foo/bar/placeholder.jpg"
}
Other times I will want to show an edited version of the image. How can I tell QQuickImageProvider::requestImage() I want to show an edited version?
QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
// Somehow determine we need to show an editted version of the image
if (showEdited) {
// maybe pass query parameters to the id?
// for eg 'image://provider/foo/bar/i.jpg?edit=true'
// Then I parse the id string for this query parameter?
cv::Mat src = cv::imread(id.toStdString());
// ... perform some image processing to the image
QImage img = convertMatToQImage(src);
if (size)
*size = QSize(img.width(), img.height());
return img;
}
else {
QImage img(id);
if (size)
*size = QSize(img.width(), img.height());
return img;
}
}
The trigger to show the edited image is by clicking a button:
Button {
id: processBtn
text: qsTr("Process")
onClicked: {
// Somehow call QQuickImageProvider::requestImage() and specify we are editting it?
// Maybe...
// image.source = image.source + "?edit=true"
}
}
I could pass the url of the image, and use the QUrl class to get that edit=true as I show below:
QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(requestedSize)
QUrl url(id);
bool showEdited = url.query() == "edit=true";
if (showEdited) {
cv::Mat src = cv::imread(url.toLocalFile().toStdString());
///begin process
cv::GaussianBlur(src, src, cv::Size(3,3), 0, 0, cv::BORDER_DEFAULT );
cv::Mat grad_x, grad_y;
cv::Mat abs_grad_x, abs_grad_y, src_gray, grad;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
cv::cvtColor( src, src_gray, CV_BGR2GRAY );
/// Gradient X
cv::Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, cv::BORDER_DEFAULT );
/// Gradient Y
cv::Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, cv::BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );
convertScaleAbs( grad_y, abs_grad_y );
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
///end process
QImage img = convertMatToQImage(grad);
if (size)
*size = QSize(img.width(), img.height());
return img;
}
else {
QImage img(url.toLocalFile());
if (size)
*size = QSize(img.width(), img.height());
return img;
}
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
import QtQuick.Dialogs 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property string path: ""
onPathChanged: image.source = path == "" ? "": "image://provider/"+ path
ColumnLayout {
anchors.fill: parent
Image {
id: image
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height * 0.8
fillMode: Image.PreserveAspectFit
}
Pane {
id: pane
Layout.fillWidth: true
RowLayout {
width: parent.width
Button {
id: selectBtn
text: qsTr("Select")
Layout.alignment: Qt.AlignHCenter
onClicked: fileDialog.open();
}
Button {
id: processBtn
text: qsTr("Process")
Layout.alignment: Qt.AlignHCenter
onClicked: if(path != "") image.source = "image://provider/"+ path + "?edit=true"
}
}
}
}
FileDialog {
id: fileDialog
title: "Please choose a file"
folder: shortcuts.home
nameFilters: [ "Image files (*.jpg *.png)", "All files (*)" ]
onAccepted: path = fileDialog.fileUrl
}
}
Original:
Process:
The complete example can be found on this link.
Use id without extension something like that:
Button {
property bool edit: false
onClicked: {
image.source = "image:://provider/foo/path" + (edit ? placeholder-edit : placeholder )
}
}
But you can implement you QQuickPaintedItem with custom properties without image provider

QML: Bind loop detected without double assignment

As far as I know the bind loop happens when I try to assign two properties each other. Example:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
Settings.someSetting = checked;
}
}
but in my scenario I can't see such a "double assignment". I report here the full code:
import QtQuick 2.7
import QtQuick.Window 2.3
Window {
visible: true;
width: 500
height: 500
Rectangle {
id: main
anchors.fill: parent
color: "black"
property bool spinning: true
property bool stopping: false
Rectangle {
x: 0.5 * parent.width
y: 0.5 * parent.height
width: 10
height: 200
radius: 5
color: 'red'
transformOrigin: Item.Top
rotation: {
if (main.stopping)
{
main.spinning = false;
main.stopping = false;
}
return timer.angle
}
}
Timer {
id: timer
interval: 5
repeat: true
running: true
onTriggered: {
if (main.spinning) angle += 1;
}
property real angle
}
MouseArea {
id: control
anchors.fill: parent
onClicked: {
main.stopping = true;
}
}
}
}
When you click with the mouse you will get the warning:
qrc:/main.qml:17:9: QML Rectangle: Binding loop detected for property "rotation"
I don't see my mistake. I'm using flags (bool variables) to control the execution of my code. I know in this case I can just stopping the timer directly, but the actual program is more complex than this example.
The binding is in the following lines:
rotation: {
if (main.stopping)
{
main.spinning = false;
main.stopping = false;
}
return timer.angle
}
The change of rotation is triggered by the change of main.stopping: let's say that change main.stopping is given by the mouseArea, then it will be called a rotation, but inside this there is an if, and in this you are changing back to main.stopping , where he will call rotation back.
If a property in QML changes all the elements that depend on it will change

QTimer::singleShot equivalent for QML

Consider this C++ statement (example from docs):
QTimer::singleShot(600000, &app, SLOT(quit()));
How to do the same in .qml JavaScript, something like this QML:
Rectangle {
property int counter: 0
onCounterChanged: {
if (counter > 42) {
// do equivalent of above C++ statement here
}
}
// more code, which actually manipulates counter
}
There's the obvious solution of having separate Timer, which is then started by this JavaScript code, and I'll accept that as an answer if a one-liner is not possible. Is it?
I ended up adding this to my main.qml:
Component {
id: delayCallerComponent
Timer {
}
}
function delayCall( interval, callback ) {
var delayCaller = delayCallerComponent.createObject( null, { "interval": interval } );
delayCaller.triggered.connect( function () {
callback();
delayCaller.destroy();
} );
delayCaller.start();
}
Which can be used like this:
delayCall( 1000, function () { ... } );
Change "repeat" property to false for Timer object.
import QtQuick 1.0
Item {
Timer {
id: timer
interval: 600000
running: false
repeat: false
onTriggered: Qt.quit()
}
Rectangle {
property int counter: 0
onCounterChanged: {
if (counter > 42) {
timer.running = true
}
}
}
}
Here is how to do it using a SequentialAnimation element:
SequentialAnimation {
id: quitTimer
PauseAnimation { duration: 60000 }
ScriptAction { script: Qt.quit() }
}
Rectangle {
property int counter: 0
onCounterChanged: {
if (counter > 42) {
quitTimer.start()
}
}
}
If that's too ugly, make a component out of it:
// SingleshotTimer.qml
import QtQuick 2.0
SequentialAnimation {
property alias delay: delayAnim.duration
property alias script: scriptAction.script
PauseAnimation { id: delayAnim; duration: 10000 }
ScriptAction { id: scriptAction }
}
Using this new component gives what you want:
SingleshotTimer { id: timer; delay: 60000; script: Qt.quit() }
Rectangle {
property int counter: 0
onCounterChanged: {
if (counter > 42) {
timer.start()
}
}
}
Another option I came up with is to simply define a function like this in C++:
void QmlUtils::singleShot(int msec, QJSValue callback)
{
QTimer::singleShot(msec, this, [callback] () mutable {
if (callback.isCallable())
callback.call();
});
}
and I call it from QML with:
lqtUtils.singleShot(5000, () => console.log("Hello!"))
I then added that C++ function to my collection of "must-have" here: https://github.com/carlonluca/lqtutils/blob/master/lqtutils_ui.h#L53.
there is a timer component in QML
import QtQuick 2.0
Item {
Timer {
interval: 500; running: true; repeat: true
onTriggered: time.text = Date().toString()
}
Text { id: time }
}
for more details see the documentation
I set properties like this running: true; repeat: false;
Timer {
interval: 5000
running: true
repeat: false
onTriggered:console.log("Test");
}

Resources