Qt Virtual Keyboard in QQuickWidget - qt

It is possible to show Virtual Keyboard in QQuickWidget or in QWidget?
I have QWidget application and I need to have better control where VirtualKeyboard is shown.
Today I spend all my day to find a solution, unfortunately without success.

The following code shows that it is valid to use virtualkeyboard in QQuickWidget.
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickWidget>
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQuickWidget widget;
widget.setResizeMode(QQuickWidget::SizeRootObjectToView);
widget.setSource(QStringLiteral("qrc:/main.qml"));
widget.show();
return app.exec();
}
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.VirtualKeyboard 2.14
Rectangle {
id: window
width: 640
height: 480
TextEdit{
text: "Hello world"
anchors.centerIn: parent
}
InputPanel {
id: inputPanel
z: 99
x: 0
y: window.height
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
59777221.pro
QT += quickwidgets virtualkeyboard
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += main.cpp
RESOURCES += qml.qrc
├── 59777221.pro
├── main.cpp
├── main.qml
└── qml.qrc

Related

QQuickWidget: grabToImage: item's window is not visible

I'm having a problem when I try to use Item::grabToImage() qml method.
No matter which item I point to, it always says the following error:
grabToImage: item's window is not visible
I tried using the root/toplevel Item named rect too, but it didnt work.
My goal: I want to capture a rectangle sized image with the map tile and polygon draw on it
Below there's a minimal reproducible example
import QtQml 2.2
import QtLocation 5.9
import QtPositioning 5.9
import QtQuick 2.0
import QtQuick.Controls 2.4
Item {
id: rect
width: 1024
height: 768
visible: true
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: map
enabled: true
visible: true
parent: rect
gesture.enabled: true
anchors.fill: parent
plugin: mapPlugin
zoomLevel: 14
activeMapType: supportedMapTypes[3]
}
Item {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 10
height: 40
Button {
id: saveToDisk
text: qsTr("Pick")
onClicked: {
map.grabToImage(function (result) {
console.log('saving to disk..')
result.saveToFile("pick.png")
})
}
}
}
}
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQuickWidgets/QQuickWidget>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQuickWidget *q = new QQuickWidget;
q->setResizeMode(QQuickWidget::SizeRootObjectToView);
q->setSource(QUrl("main.qml"));
q->show();
return app.exec();
}
The strategy of QQuickWidget for painting is to create an off-screen QQuickWindow that renders the QML from where a screenshot is taken and drawn onto the widget. The above limits the use of grabToImage() since this method requires that the QQuickWindow of the items be visible.
The solution is to use QQuickView + QWidget::createWindowContainer():
#include <QApplication>
#include <QWidget>
#include <QQuickView>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQuickView *q = new QQuickView;
q->setResizeMode(QQuickView::SizeRootObjectToView);
q->setSource(QUrl("main.qml"));
QWidget * container = QWidget::createWindowContainer(q);
container->show();
return app.exec();
}

Issue with Material Style in QML

I have this small example that does not work as i expected :
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
Window {
id: root
visible: true
width: 640
height: 480
property bool lightTheme: false
Material.theme: Material.Dark
Material.foreground: Material.color(Material.Red) // value is always material red #F44336 (from light theme)
onLightThemeChanged: {
Material.theme = lightTheme ? Material.Light : Material.Dark;
}
Button {
id: btn
width: 200
height: 200
anchors.centerIn: parent
text: "change theme"
onClicked: {
lightTheme = !lightTheme;
}
}
Text {
id: darkRed
text: "predefinedDarkThemeRed"
color: "#EF9A9A"
anchors.top: btn.bottom
anchors.horizontalCenter: btn.horizontalCenter
}
Text {
id: lightRed
text: "predefinedLightThemeRed"
color: "#F44336"
anchors.top: darkRed.bottom
anchors.left: darkRed.left
}
}
The issue is with the default Material.Red color being always picked from the Material.Light theme whatever theme i have selected.
However, when i don't set any Material.foreground, then it is white with the Material.Dark and dark with the Material.Light, and dynamically switched between those colors when the theme is changed, so everything is fine.
I would expected the same behaviour with a custom Material.foreground but it does not seem to work.
What is wrong here ?
Thank you.
Note: the app is run with options -style material args, and i am using Qt 5.9.3 or Qt 5.10.1
i think you missed a little nuance
add in .pro file
QT += quickcontrols2
in mine file add QQuickStyle::setStyle("Material");
#include <QQuickStyle>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
................
QQuickStyle::setStyle("Material");
//The style can also be specified as a path to a custom style, such as
//QQuickStyle::setStyle(":/mystyle");
................
return app.exec();
}
in your example, the result will be
when pressed

How to use QT internationalization

This may seem like a dumb question, but i'm having lots of trouble to have internationalization working well in QT 5.12.
There are at least 2 things i would like to do:
The possibility to save the language that was selected by the user and the next time the user runs the app its selected (but problably that would require at least a file to save that), or run the app in the native language of the SO.
Having a dynamic translation working, by this i mean the language to be automatically changed while the application is running.
About the first point i know that to install the translation of the native language of the system something like Translator.load("qt_" + QLocale::system().name(),QLibraryInfo::location(QLibraryInfo::TranslationsPath)) can be used.
The second point, i've found a solution but it requires that i place a folder with the name translation(on this case) on the release/debug folder where i place the individual .qm files.
I'm going to provide a simple example of what i've found so far:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QFontDatabase>
#include <QFont>
#include <QtQml>
#include "trans.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QTranslator Translator;
Translator.load(":/translations/translating-qml_ru.qm");
app.installTranslator(&Translator);
QQmlApplicationEngine engine;
// object of our class with "magic" property for translation
Trans trans(&engine);
// make this object available from QML side
engine.rootContext()->setContextProperty("trans", &trans);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
import QtQuick.Window 2.2
ApplicationWindow {
id: root
width: 800
minimumWidth: 500
height: 600
minimumHeight: 600
visible: true
title: "Translating QML application"
Column {
width: parent.width * 0.95
spacing: 15
padding: 15
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
Button {
Layout.preferredWidth: 76
Layout.preferredHeight: 53
text:"EN"
onClicked: {
onClicked: trans.selectLanguage("en");
}
}
Rectangle
{
Layout.preferredWidth: 2
Layout.fillHeight: true
Layout.margins: 10
color: "black"
}
Button {
text: "RU"
Layout.preferredWidth: 76
Layout.preferredHeight: 53
onClicked: {
onClicked: trans.selectLanguage("ru");
}
}
Rectangle
{
Layout.preferredWidth: 2
Layout.fillHeight: true
Layout.margins: 10
color: "black"
}
Button {
text: "NO"
Layout.preferredWidth: 76
Layout.preferredHeight: 53
onClicked: {
onClicked: trans.selectLanguage("no");
}
}
Rectangle
{
Layout.preferredWidth: 2
Layout.fillHeight: true
Layout.margins: 10
color: "black"
}
Button {
text: "DE"
Layout.preferredWidth: 76
Layout.preferredHeight: 53
onClicked: {
onClicked: trans.selectLanguage("de");
}
}
}
Label {
font.pixelSize: 16
text: qsTr("I woke up after midnight and realised - <b>IT DOES</b>!<br/>"
+ "Everything goes according to the plan.")
}
}
}
trans.cpp
#include "trans.h"
Trans::Trans(QQmlEngine *engine)
{
_translator = new QTranslator(this);
_engine = engine;
}
void Trans::selectLanguage(QString language)
{
QString languagesArray[] = { "en", "pt", "es", "br", "de", "dk", "fi", "fr", "it", "lt", "no", "ro", "tr", "hu" };
QDir dir = QDir(qApp->applicationDirPath()).absolutePath();
for(int i=0;i<languagesArray->length();i++){
if(languagesArray[i] != language){
_translator->load(QString("Lang-%1").arg(languagesArray[i]),QString("%1/translation").arg(dir.path()));
qApp->removeTranslator(_translator);
}
}
if (!_translator->load(
QString("translating-qml_%1").arg(language),
// look for the file in translations folder within working directory
QString("%1/translations").arg(dir.path())
)
)
{
qDebug() << "Failed to load translation file, falling back to English";
}
// it's a global thing, we can use it anywhere (after #including <QGuiApplication>)
qApp->installTranslator(_translator);
_engine->retranslate();
emit languageChanged();
}
trans.h
#ifndef TRANS_H
#define TRANS_H
#include <QObject>
#include <QTranslator>
#include <QDebug>
#include <QGuiApplication>
#include <QDir>
#include <QQmlEngine>
class Trans : public QObject
{
Q_OBJECT
public:
Trans(QQmlEngine *engine);
Q_INVOKABLE void selectLanguage(QString language);
signals:
void languageChanged();
private:
QTranslator *_translator;
QQmlEngine *_engine;
};
#endif // TRANS_H
I want to know step by step what i have to do to have this system fully working, because the info i've found the web (including qt docs) is confuse to me.
For the first point it is only necessary to save the information that identifies the language on the hard disk through QSettings. When the application is started, the QSettings should be read and, accordingly, the translation should be done and when the language is modified it should be saved.
Your second point is not very clear, but I suppose you want what are the steps to use Qt Internationalization, there are several methods since some tasks can be done manually and others can be automated.
First add the following instruction to the .pro:
TRANSLATIONS = /path/of/some_name1.ts \
/path/of/some_name2.ts \
/path/of/some_name3.ts
In my example I use the following structure:
TARGET = AppTranslations
# ...
TRANSLATIONS = i18n/$${TARGET}_en.ts \
i18n/$${TARGET}_de.ts \
i18n/$${TARGET}_no.ts \
i18n/$${TARGET}_ru.ts
Then you must open the terminal or CMD in the folder where your .pro is and execute the following command:
lupdate your_project.pro
This generates the .ts where you indicated, then you have to use Qt Linguist to edit that file doing the translations.
Then you convert the .ts to .qm with:
lrelease your_project.pro
Then you can add the .qm to a qresource embedding in the application, but in my case I prefer that it is in a folder on the side of the executable so the executable does not weigh much and could add more translations without recompiling the project, and for it to be automated the next command to be copied to the side of the executable.
COPY_CONFIG = $$files(i18n/*.qm, true)
copy_cmd.input = COPY_CONFIG
copy_cmd.output = i18n/${QMAKE_FILE_IN_BASE}${QMAKE_FILE_EXT}
copy_cmd.commands = $$QMAKE_COPY_DIR ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
copy_cmd.CONFIG += no_link_no_clean
copy_cmd.variable_out = PRE_TARGETDEPS
QMAKE_EXTRA_COMPILERS += copy_cmd
So in the end the build folder will have the following structure:
├── AppTranslations
├── i18n
│   ├── AppTranslations_de.qm
│   ├── AppTranslations_en.qm
│   ├── AppTranslations_no.qm
│   └── AppTranslations_ru.qm
│  ...
Now that you have the .qm, the logic of using it in the application is implemented. Before Qt 5.10 you had to do the trick of adding an empty string for the translation to work but the latest versions do not require it.
On the other hand in my case I implement a logic to get the .qm and so the languages available since I use a default format:
{Name_Of_Application}_{lang}.qm
The other part of the logic is similar to yours so I will not go into much detail and I will show you the code:
translator.h
#ifndef TRANSLATOR_H
#define TRANSLATOR_H
#include <QDir>
#include <QObject>
#include <QQmlEngine>
#include <QTranslator>
class Translator : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList languages READ languages NOTIFY languagesChanged)
Q_PROPERTY(QString currentLanguage READ currentLanguage NOTIFY currentLanguageChanged)
public:
explicit Translator(QQmlEngine *engine, QObject *parent = nullptr);
Q_INVOKABLE void selectLanguage(const QString & language);
QStringList languages() const;
QString currentLanguage() const;
Q_INVOKABLE static QString languageByCode(const QString & code);
signals:
void languageChanged();
void languagesChanged();
void currentLanguageChanged();
private:
const QString extension = ".qm";
QQmlEngine *m_engine;
QTranslator *m_translator;
QStringList m_languages;
QString m_currentLanguage;
QDir m_dir;
};
#endif // TRANSLATOR_H
translator.cpp
#include "translator.h"
#include <QGuiApplication>
#include <QDirIterator>
#include <QSettings>
Translator::Translator(QQmlEngine *engine, QObject *parent) :
QObject(parent),
m_engine(engine)
{
m_translator = new QTranslator(this);
m_dir = QDir(QGuiApplication::applicationDirPath(),
"*"+extension,
QDir::Name|QDir::IgnoreCase,
QDir::Files);
m_dir.cd("i18n");
m_languages.clear();
for(QString entry: m_dir.entryList()){
entry.remove(0, QGuiApplication::applicationName().length()+1);
entry.chop(extension.length());
m_languages.append(entry);
}
emit languagesChanged();
QSettings settings;
QString lang =settings.value("Language/current", QLocale::system().bcp47Name()).toString();
selectLanguage(lang);
}
QStringList Translator::languages() const
{
return m_languages;
}
QString Translator::currentLanguage() const
{
return m_currentLanguage;
}
QString Translator::languageByCode(const QString &code)
{
QLocale lo(code);
return QLocale::languageToString(lo.language());
}
void Translator::selectLanguage(const QString &language)
{
qApp->removeTranslator(m_translator);
if(m_languages.contains(language)){
QString file = QString("%1_%2%3").arg(QGuiApplication::applicationName()).arg(language).arg(extension);
if(m_translator->load(m_dir.absoluteFilePath(file))){
m_currentLanguage = language;
QSettings settings;
settings.setValue("Language/current", language);
emit currentLanguageChanged();
}
}
qApp->installTranslator(m_translator);
m_engine->retranslate();
emit languageChanged();
}
And then it applies to your project:
main.cpp
#include "translator.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setOrganizationName("Translations INC");
QCoreApplication::setOrganizationDomain("translations.com");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Translator trans(&engine);
engine.rootContext()->setContextProperty("trans", &trans);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
import QtQuick.Window 2.2
ApplicationWindow {
id: root
width: 800
minimumWidth: 500
height: 600
minimumHeight: 600
visible: true
title: "Translating QML application"
Column {
width: parent.width * 0.95
spacing: 15
padding: 15
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
Repeater{
model: trans.languages
Button{
id: btn
property string code: modelData
text: trans.languageByCode(code)
onClicked: trans.selectLanguage(btn.code)
Layout.preferredWidth: 100
Layout.preferredHeight: 50
highlighted: code == trans.currentLanguage
}
}
}
Label {
font.pixelSize: 16
text: qsTr("I woke up after midnight and realised - <b>IT DOES</b>!<br/>"
+ "Everything goes according to the plan.")
}
}
}
The complete example you find here.

Accept/reject signals of dialog not catched

Upgrading Qt to v5.10.1 the Dialog doesn't emit accept/reject signals. The last known version that works smooth was 5.10.0. My question is - does I miss something or I use some component in wrong way? Or it is regression (and I will report it?)
Issue reproduced on macOS 10.12+ & Win10 (VC 2015/2017)
Simplified source code sample:
CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(bug-test LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Core Quick REQUIRED)
add_executable(${PROJECT_NAME} "main.cpp" "qml.qrc")
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick)
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty()) return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: applicationWindow
visible: true
Button {
text: qsTr("Push me!")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
onClicked: dlgLoader.source = "qrc:/MsgDialog.qml"
}
Loader {
id: dlgLoader
onStatusChanged: {
if (dlgLoader.status == Loader.Ready) {
item.parent = ApplicationWindow.overlay
item.open()
}
}
}
Connections {
target: dlgLoader.item
onClosed: dlgLoader.source = ""
}
}
MsgDialog.qml
import QtQuick 2.9
MsgDialogForm {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: Math.min(applicationWindow.width, applicationWindow.height) / 5 * 4
onAccepted: console.log("Ok clicked; save answer")
onRejected: console.log("Cancel clicked; don't save")
}
MsgDialogForm.ui.qml
import QtQuick 2.9
import QtQuick.Controls 2.3
Dialog {
id: dialog
modal: true
standardButtons: Dialog.Yes | Dialog.No
closePolicy: Popup.CloseOnEscape
}
qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>qtquickcontrols2.conf</file>
<file>MsgDialog.qml</file>
<file>MsgDialogForm.ui.qml</file>
</qresource>
</RCC>
Just start the app; push the button and click one of dialog buttons. Nothing is shown in QtCreator console but dialog is closed.
When a new item is established in the Loader the previous item is deleted from the memory, in your case you are doing it when the window is closed, but the accepted or rejected signal is emited after the window is closed.
A possible solution is to create a signal that is emited after receiving the message.
MsgDialog.qml
import QtQuick 2.9
MsgDialogForm {
signal finished()
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: Math.min(applicationWindow.width, applicationWindow.height) / 5 * 4
onAccepted: {
console.log("Ok clicked; save answer")
finished()
}
onRejected: {
console.log("Cancel clicked; don't save")
finished()
}
}
main.qml
...
Connections {
target: dlgLoader.item
onFinished: dlgLoader.source = ""
}

Run the QtQuick application

I create a project on QtQuick in QT Creator 4.3.1 without using the ui form.
Here's the code main.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
}
}
TextEdit {
id: textEdit
text: qsTr("Enter some text...")
verticalAlignment: Text.AlignVCenter
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 20
Rectangle {
anchors.fill: parent
anchors.margins: -10
color: "transparent"
border.width: 1
}
}
}
The program runs and works.
Now I want to get rid of Window and replace it with Rectangle:
import QtQuick 2.6
Rectangle {
id: root
width: 200; height: 200;
color: "#ffffff"
}
But when the program starts, nothing happens, the form does not open
What am I doing wrong?
main.cpp code. code in the same in both cases:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
In a Qt application you must have at least one window that is the top-level, that is, the window where other components are placed.
When you create your project, you should get the default main.cpp:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
So let's analyze the elements and use the documentation for it:
QQmlApplicationEngine:
QQmlApplicationEngine provides a convenient way to load an application from a single QML file.
[...]
Unlike QQuickView, QQmlApplicationEngine does not automatically create a root window. If you are using visual items from Qt Quick, you will need to place them inside of a Window.
That is, QQmlApplicationEngine does not create a top-level, so if we want the window to be shown you must use another element, and as recommended, an option is to use Window{}
In your second test you are using an item, e.g. Rectangle, and this is only a component and is not able to create a top-level, so for that it is advisable to use QQuickView:
The QQuickView class provides a window for displaying a Qt Quick user interface.
[...]
So if you want to show the Rectangle you should use the following in your main.cpp:
main.cpp
#include <QGuiApplication>
#include <QQuickView>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.show();
return app.exec();
}
main.qml
import QtQuick 2.6
Rectangle {
id: root
width: 200; height: 200;
color: "#ffffff"
}

Resources