QQuickView, no keyboard focus after switching windows - qt

My cpp application has a QMainWindow derived class, with QQuickView widget in the ui. Inside the view are a number of QML items which accept keyboard input. When an item is clicked, I call forceActiveFocus() on the clicked item. It all works from the time I launch the application, until the time I switch to another window.
If I switch to another application and back, or switch to another window within my application and back, calling forceActiveFocus() has no effect. The items are of a few different types, but here is a sample item:
TextInput {
id: textInput
anchors.fill: parent
inputMethodHints: Qt.ImhFormattedNumbersOnly
onActiveFocusChanged: console.log(activeFocus)
onEditingFinished:
{
}
MouseArea {
anchors.fill: textInput
onClicked: {
textInput.forceActiveFocus()
console.log("clicked")
}
}
}
When switching away I see activeFocus logged to the console as false. When I switch back again and click on the item, "clicked" is logged to the console, so the mouse event is being handled. However, onActiveFocusChanged is never called again.
I also tried an implementation with a FocusScope as the parent of the item, got the same behavior, with focus following my click until the point I switch away to some other window and back again.
UPDATE
After reading the comment from #user2436719, I've tried two minimal examples. It is only when using the QQuickView that this problem arises. Here is the QML app using a QML Window with the following main.qrc, which works just fine:
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
color: "lightblue"
Rectangle {
id: textInputRect
color: "white"
height: 50
width: 150
anchors.centerIn: parent
TextInput {
id: textInput
anchors.fill: parent
inputMethodHints: Qt.ImhFormattedNumbersOnly
onActiveFocusChanged: console.log(activeFocus)
onEditingFinished:
{
}
}
}
}
The second is a CPP application with a QMainWindow derived class that has a QQuickView widget in the ui. This version exhibits the problem behavior. Here is the main.qrc:
import QtQuick 2.7
import QtQuick.Window 2.2
Rectangle {
anchors.fill: parent
color: "lightblue"
Rectangle {
id: textInputRect
color: "white"
height: 50
width: 150
anchors.centerIn: parent
TextInput {
id: textInput
anchors.fill: parent
inputMethodHints: Qt.ImhFormattedNumbersOnly
onActiveFocusChanged: console.log(activeFocus)
onEditingFinished:
{
}
}
}
}
From the QQuickView version, here is main:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
MainWindow window;
QQuickView* view = new QQuickView;
window.setView(view);
window.show();
return app.exec();
}
And here is how I set the QQuickView in the MainWindow:
void MainWindow::setView(QQuickView *value)
{
view = value;
QWidget *container = QWidget::createWindowContainer(view, this);
view->setSource(QUrl("qrc:/main.qml"));
ui->verticalLayout->addWidget(container);
}

Okay, this is QTBUG-34414. At the bottom of the comments for that bug a workaround is posted. The posted workaround fixed the problem for me, in the case of switching to other windows of my application, or to other applications. There was still no focus after closing dialogs. Putting this override in my window class fixed the problem for both cases:
bool MyWindow::event(QEvent *event)
{
if (event->type() == QEvent::ActivationChange ||
event->type() == QEvent::WindowUnblocked) {
if(view->isActive()) { //view is pointer to my QQuickView
window()->activateWindow();
return true;
}
}
// handle events that don't match
return QWidget::event(event);
}
This override worked for me with Qt 5.7 on OSX Sierra.

Related

Create multiple instances of QSortFilterProxyModel for view

I have two listviews on a single Page component. The model for both is coming from a single QSortFilterProxyModel. The problem is if I set data for one ListView, the other one is also changed. This happens as there is a single instance of the model.
Will I have to create 2 different instances of the QSortFilterProxyModel or there is some other way around?
My Code
main.cpp
int main(int argc, char *argv[])
{
// Application basic initialization
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QtWebEngine::initialize();
QQuickStyle::setStyle("Default");
FilterModel filterModel;
FilterList filterList;
// Set contexts for QML
engine.rootContext()->setContextProperty("filterModel",&filterModel);
engine.rootContext()->setContextProperty("filterList",&filterList);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
filterlist.cpp
#include "filterlist.h"
FilterList::FilterList(QObject *parent) : QSortFilterProxyModel(parent)
{
setSourceModel(&m_filterListModel);
}
void FilterList::searchByCategory(QString filterSubCategory)
{
setFilterRole(m_filterListModel.FilterListCategoryRole);
this->setFilterCaseSensitivity(Qt::CaseInsensitive);
this->setFilterFixedString(filterSubCategory);
}
mypage.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Page {
id : somepageid
Column{
Button{
id: btn1
text: "btn a"
onClicked: {
filterList.searchByCategory("category a")
}
}
Button{
id: btn2
text: "btn b"
onClicked: {
filterList.searchByCategory("category b")
}
}
}
ListView{
id: lv1
model: filterList
height: 100
delegate: Row{
Text{
text: name
}
}
}
ListView{
id: lv2
anchors.top: lv1.bottom
model: filterList
height: 100
delegate: Row{
Text{
text: name
}
}
}
}
Will I have to create 2 different instances of the
QSortFilterProxyModel or there is some other way around?
Even if you create 2 instances of proxy models, you will experience the same problem if you set the same source for both. When you call QSortFilterProxyModel::setSource, it connects proxy model to source model and everything you change will be propagated to source model. So if you change something in 1st proxy model it will propagate to source model and from source model to 2nd proxy model.
Documentation citation QSortFilterProxyModel:
Any changes made through the QSortFilterProxyModel are applied to the original model.
So in order to have 2 independend lists with initially the same models, you have to create 2 instances of source model and 2 instances of the proxy model.

Showing a popup without any toplevel window in QtQuick

I want to make a game overlay to show my custom cross-hair. I want it to be always there with no closing policy.
I used a Popup item in an ApplicationWindow and I set the opacity of it to 0 and the opacity of the pop-up to 1 but it just showed me nothing.I also tried using the pop-up item without any toplevel window but again nothing.
The question is "Is it possible to show an always-on-top popup without any visible top-level window ?"
Your suggestions would be appreciated.
Without a popup window, we can try out with the 'z' property of the QQuickItem. Stack the crosshair always on top of your other items. And if you want to move the crosshair across the screen you can use their 'x,y' properties.
I have tried a simple sample for the same. Used an Image item for crosshair on top of the scroll view. It works as expected. I tried my way. We will see if some other idea's coming in.
Sample code here:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 640
height: 480
// used here only to indicate image loading is going on
BusyIndicator {
id: busyindicatorId
visible: backgroundImgId.status === Image.Loading ||
crossImgId.status === Image.Loading
anchors.centerIn: parent
}
// background item
ScrollView {
anchors.fill: parent
anchors.margins: 10
clip: true
visible: !busyindicatorId.visible
Image {
id: backgroundImgId
source: "https://i.ibb.co/ZBNLvzb/andriod.jpg"
}
}
// crosshair item
Image {
id: crossImgId
z: 1
width: 100
height: width
visible: !busyindicatorId.visible
source: "https://i.ibb.co/SJFTLwN/cross.png"
anchors.centerIn: parent
}
}
Updates
Instantiated two windows of that one can be used for crosshair and have to set some window properties(transparent, alwaysontop) to show always on top. Let's have a look at this base code. Sample video
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QScreen>
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;
QScreen *screen = QGuiApplication::primaryScreen();
QQuickView view;
view.setSource(QUrl(QStringLiteral("qrc:/crosshair.qml")));
view.setX(screen->geometry().width()/2 - view.width()/2);
view.setY(screen->geometry().height()/2 - view.height()/2);
view.setColor("transparent");
view.setFlags(Qt::SubWindow | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
view.show();
return app.exec();
}
//////////////// main.qml ////////////////
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
visibility: "FullScreen"
objectName: "mainWindow"
// used here only to indicate image loading is going on
BusyIndicator {
id: busyindicatorId
visible: backgroundImgId.status === Image.Loading
anchors.centerIn: parent
}
// background item
ScrollView {
anchors.fill: parent
anchors.margins: 10
clip: true
visible: !busyindicatorId.visible
Image {
id: backgroundImgId
source: "https://i.ibb.co/ZBNLvzb/andriod.jpg"
}
}
}
//////////////// crosshair.qml ////////////////
import QtQuick 2.9
import QtQuick.Controls 2.2
Item {
width: 100
height: 100
// crosshair item
Image {
width: parent.width
height: parent.height
source: "https://i.ibb.co/SJFTLwN/cross.png"
}
}

How to get signal key pressed on QT virtual keyboard and play a sound click track?

in my QT application for embedded device , i want play a sound on key pressed event of the QML virtualkeyboard . Can I get this event? and How get it?
I already have a class that play sound ( click effect) when a button was clicked that i use in the others qml pages
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import QtQuick.VirtualKeyboard 2.1
Page{
id: pag
width: 1280
height: 800
background: Rectangle { color: "black"}
TextField {
id: txtName
height: 200
width:200
anchors.horizontalcenter:parent.horizontalCenter
font.family: "Arial
font.pixelSize: 24
placeholderText: "insert your text here"
background: Rectangle {
anchors.fill: parent
color: "transparent"
}
}
InputPanel {
id: virtualkeyboard
width: 0.95*parent.width
anchors.bottom: parent.bottom
}
}
You can create a class Mykeyfilter, it's a QObject class
then in your file .h you declare:
bool eventFilter(QObject *object, QEvent *event);
Then in your Mykeyfilter.cpp file you define eventFilter like this:
bool MykeyFilter::eventFilter(QObject *object, QEvent *event)
{
switch(event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
//////call your sound class here that you want to play/////
qDebug()<<"I have clicked" //For testing
}
default:
break;
// return QObject::eventFilter(object, event);
}
return QObject::eventFilter(object, event);
}
Also add in your main.cpp file:
#include "mytouchfilter.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
app.installEventFilter(new MykeyFilter());
return app.exec();
}
You have two options:
Use the clicked() signal of BaseKey.
Use the soundEffect property of KeyPanel.
Both require having your own style. You can read more about creating your own style here. To quote from there:
A good starting point for creating a new style is to use an existing built-in style as a template and edit it. You can find the built-in styles from the virtual keyboard sources directory src/virtualkeyboard/content/styles. Copy one of the directories containing a built-in style into the Styles directory and rename it to "test". [...]

Avoid QQuickView delayed qml loading

Here's my situation. I'm trying to combine qml with a mostly widget based UI. To do this, I'm using QQuickView with QWidget::createWindowContainer. I can't use QQuickWidget, because I need to convert the window into a native window, and QQuickWidget doesn't like that. But back to the issue.
My problem is that the first time the view is displayed, it takes like half a second to load causing a very obvious flicker. After that I can hide/show the view all I want, it displays immediately. It's only the first time the qml loads. And I'm fairly certain it's the loading of the qml that causes the issue. Because I have two different QQuickViews that get the same qml as their source. But after any one of them loads once, the other has no issues displaying instantly.
I tried to call show() on view early to get it to load in time. But this causes the qml to appear for a brief moment before any of the widgets get displayed.
Has anyone encountered a similar issue? How can I get the QQuickView to behave.
Edit: I'm using Qt 5.4.2, and I can't update to a newer version due to various reasons.
I was going to say that you can use the same approach as in this answer, but it seems that even that is too early to being loading the QML. It's hacky, but the only other thing I can think of is using a very short Timer:
main.cpp:
#include <QtWidgets>
#include <QtQuick>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent)
{
QQuickView *view = new QQuickView();
QWidget *container = QWidget::createWindowContainer(view, this);
container->setFocusPolicy(Qt::TabFocus);
view->rootContext()->setContextProperty("window", view);
view->setSource(QUrl("qrc:/main.qml"));
setCentralWidget(container);
resize(400, 400);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
Item {
anchors.fill: parent
// Connections {
// target: window
// onAfterSynchronizing: loader.active = true
// }
Timer {
running: true
repeat: true
interval: 50
onTriggered: {
loader.active = true
}
}
Loader {
id: loader
active: false
sourceComponent: Column {
anchors.fill: parent
Repeater {
model: 30000
delegate: Button {
text: index
}
}
}
}
BusyIndicator {
running: loader.status === Loader.Null
anchors.centerIn: parent
}
}
For brevity, I chucked the "heavy" QML into sourceComponent, but you can also use the source property to point to a URL.
BusyIndicator runs its animation on the render thread, so it can continue to spin while the GUI thread is blocked.

QML DropArea accept external drag

I've noticed a new DropArea component in Qt5. I'm trying to drag a file from Finder (Mac) but only onEntered method is called.
import QtQuick 2.0
Rectangle {
id: background;
color: "white";
width: 300;
height: 300;
DropArea {
id: dropArea;
anchors.fill: parent;
onEntered: {
background.color = "gray";
drag.accept (Qt.CopyAction);
console.log("onEntered");
}
onDropped: {
console.log ("onDropped");
}
onExited: {
bckground.color = "white";
console.log ("onExited");
}
}
}
and here is window creation code:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQuickView qmlView;
qmlView.setGeometry(0, 200, 600, 400);
qmlView.setResizeMode (QQuickView::SizeRootObjectToView);
qmlView.setSource(QUrl::fromLocalFile("/Users/ivann/Projects/QtGuiTestApp/testView.qml"));
qmlView.show();
return a.exec();
}
Am I missing something?
Seems to be a Mac-specific issue (it wokrs as expected on Linux at least). Filled a bugreport to Nokia: https://bugreports.qt.io/browse/QTBUG-27125
As stated in the link attached by chebum that feature is not supported by QtQuick on any platform at the time of this writing.
Only posting for letting it know to future readers.
QtQuick 5.2 supports drag and drop from external applications. See the example http://qt-project.org/doc/qt-5/qtquick-externaldraganddrop-example.html

Resources