I'm using qt widgets on embedded device and have problem with virtual keyboard. Keyboard is shown as fullscreen and overlaps all app.
In article Virtual keyboard top black screen in Yocto is described hack how to solve this issue.
In short, you need to find the QQuickWindow with the keyboard and call setMask on this window. Then the area above the keyboard will be transparent
I have problem how to find QQuickWindow with virtual keyboard. I tried to use
QApplication::allWidgets()
but the window isn't here.
To obtain all the windows you can use QGuiApplication::allWindows() but that is not enough since the QtVirtualKeyboard window is not necessarily created at the beginning, so the visibleChanged signal of the QInputMethod must be used. I did not filter using the information from the QQuickWindow since in general the application could have others, instead it uses the name of the class to which the window belongs.
#include <QApplication>
#include <QWindow>
#include <cstring>
static void handleVisibleChanged(){
if (!QGuiApplication::inputMethod()->isVisible())
return;
for(QWindow * w: QGuiApplication::allWindows()){
if(std::strcmp(w->metaObject()->className(), "QtVirtualKeyboard::InputView") == 0){
if(QObject *keyboard = w->findChild<QObject *>("keyboard")){
QRect r = w->geometry();
r.moveTop(keyboard->property("y").toDouble());
w->setMask(r);
return;
}
}
}
}
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QApplication a(argc, argv);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::visibleChanged, &handleVisibleChanged);
// ...
Python version:
import os
import sys
from PySide2 import QtCore, QtGui, QtWidgets
# from PyQt5 import QtCore, QtGui, QtWidgets
def handleVisibleChanged():
if not QtGui.QGuiApplication.inputMethod().isVisible():
return
for w in QtGui.QGuiApplication.allWindows():
if w.metaObject().className() == "QtVirtualKeyboard::InputView":
keyboard = w.findChild(QtCore.QObject, "keyboard")
if keyboard is not None:
r = w.geometry()
r.moveTop(keyboard.property("y"))
w.setMask(QtGui.QRegion(r))
return
def main():
os.environ["QT_IM_MODULE"] = "qtvirtualkeyboard"
app = QtWidgets.QApplication(sys.argv)
QtGui.QGuiApplication.inputMethod().visibleChanged.connect(handleVisibleChanged)
w = QtWidgets.QLineEdit()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have a solution for people who use Raspberry with PyQt5.
To complete the answer of eyllanesc because the Python version does not work with PyQt5, in fact, we have this problem :
TypeError: setMask(self, QRegion): argument 1 has unexpected type
'QRect'
To solve it :
def handleVisibleChanged():
if not QtGui.QGuiApplication.inputMethod().isVisible():
return
for w in QtGui.QGuiApplication.allWindows():
if w.metaObject().className() == "QtVirtualKeyboard::InputView":
keyboard = w.findChild(QtCore.QObject, "keyboard")
if keyboard is not None:
region = w.mask()
rect = [w.geometry()]
rect[0].moveTop(keyboard.property("y"))
region.setRects(rect)
w.setMask(region)
return
You can use findChildren with any class that inherits QObject such as QApplication. For example in main.cpp:
QApplication a(argc, argv);
QList<QQuickWindow *> wind = a.findChildren<QQuickWindow *>();
This will give you a list of pointers pointing to all the QQuickWindow in your application.
Related
I have 3 files (along with CMakeLists.txt and qrc):
main.cpp - C++
BDialog.qml - a "base" for my dialog (contains the property definition)
BEditorDialog.qml - a dialog with a label
They contain:
// main.cpp
#include <QApplication>
#include <QQuickView>
#include <QQmlContext>
int main( int argc, char **argv )
{
QApplication app( argc, argv );
QQuickView view;
view.rootContext()->setContextProperty( "myText", "WOW!" ); // expected to work but doesn't
view.setSource( QUrl( "qrc:/BEditorDialog.qml" ) );
view.show();
return app.exec();
}
//BDialog.qml
import QtQuick 2.6
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
Item {
// This property I want to set from C++
property string myText
}
// BEditorDialog.qml
import QtQuick 2.6
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
BDialog {
id: root
Label {
id: label
text: root.myText // doesn't work
}
}
If I assign initial value to the myText property:
property string myText: "MEH!"
this initial value is displayed, but how to assign it from C++ ? I suspect I should use the setInitialProperties member function, but I have only Qt 5.12 installed.
Thank you in advance.
You've mixed up context and object. You're setting the property "myText" on the QQmlContext which is not the root object of your application. To get the root object you can use rootObject() on your QQuickView and get a QQuickItem back. On that you can call setProperty().
This works
#include <QApplication>
#include <QQmlContext>
#include <QQuickItem>
#include <QQuickView>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl("qrc:/BEditorDialog.qml"));
// First set the source and then access the rootObject,
// otherwise it isn't created yet
view.rootObject()->setProperty("myText", "WOW!");
view.show();
return app.exec();
}
In addition to the answer of iam_peter, you can actually also use the context property in the way you have set it, but with the warning that a name collision is present. This might be more appropriate in bigger projects (it seems here you only want a way to show a dialog from C++ and be done with it)
Given a proper name, say 'myDialogText', you can use it as follows:
// BEditorDialog.qml
import QtQuick 2.6
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
BDialog {
id: root
Label {
id: label
text: myDialogText
}
}
I need to parse a QML tree and get ids of all QML objects along the way which have it. I noticed that ids don't behave like normal properties (see the example below) – value returned from obj->property call is an invalid QVariant.
My question is – is there a way to retrieve object's id, even in some hacky (but reproductible) way?
Simplified example:
main.qml:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
Item {
id: howToGetThis
objectName: "item"
}
}
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QTimer>
#include <QDebug>
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;
QTimer::singleShot(1000, [&]() {
auto item = engine.rootObjects()[0]->findChild<QObject*>("item");
qDebug() << item->property("objectName");
qDebug() << item->property("id");
});
return app.exec();
}
Output:
QVariant(QString, "item")
QVariant(Invalid)
I think what you need is:
QString QQmlContext::nameForObject(QObject *object)
You can find the description here:
https://doc.qt.io/qt-5/qqmlcontext.html#nameForObject
Returns the name of object in this context, or an empty string if object is not named in the context. Objects are named by setContextProperty(), or by ids in the case of QML created contexts.
Based on comments received, a common pitfall is to call nameForObject using the wrong QQmlContext. (When that happens, you just the empty string.) To help with that, here is a more complete example:
QQuickItem* const focus_item = my_QQuickWindow->activeFocusItem();
if (!focus_item) {
fprintf(stderr, "no item has focus");
} else {
// There are many contexts in a hierarchy. You have to get the right one:
QQmlContext* const context = qmlContext(focus_item);
if (!context) {
// Unsure if this branch of code is even reachable:
fprintf(stderr, "item is not in any context?");
} else {
const QString focus_item_id = context->nameForObject(focus_item);
fprintf(stderr, "focus item: %s\n", focus_item_id.toStdString().c_str());
}
}
I'm trying to get my QMainWindow to allow only tabbed QDockWidgets. If I understood the Qt Documentation right it should work with the setDockOptions-method.
The following code didn't work for me:
QMainWindow window;
window.setDockOptions(QMainWindow::ForceTabbedDocks);
What am I doing wrong? Or is it a bug in the current Qt version? I'm coding on a MacPro an I'm using Qt 5.7.
thanks
ForceTabbedDocks only applies to user interactions with the docks.
To programatically add new docks in tabs, you need to use QMainWindow::tabifyDockWidgets. For example,
void MainWindow::addTabbedDock(Qt::DockWidgetArea area, QDockWidget *widget)
{
QList<QDockWidget*> allDockWidgets = findChildren<QDockWidget*>();
QVector<QDockWidget*> areaDockWidgets;
for(QDockWidget *w : allDockWidgets) {
if(dockWidgetArea(w) == area) {
areaDockWidgets.append(w);
}
}
if(areaDockWidgets.empty()) {
// no other widgets
addDockWidget(area, widget);
} else {
tabifyDockWidget(areaDockWidgets.last(), widget);
}
}
This is the same answer as #Xian Nox, but adapted for python:
def addTabbedDock(self, area: QtCore.Qt.DockWidgetArea, dockwidget: QtWidgets.QDockWidget):
curAreaWidgets = [d for d in self.findChildren(QtWidgets.QDockWidget)
if self.dockWidgetArea(d) == area]
try:
self.tabifyDockWidget(curAreaWidgets[-1], dockwidget)
except IndexError:
# First dock in area
self.addDockWidget(area, dockwidget)
How do I show a message box with Yes/No buttons in Qt, and how do I check which of them was pressed?
I.e. a message box that looks like this:
You would use QMessageBox::question for that.
Example in a hypothetical widget's slot:
#include <QApplication>
#include <QMessageBox>
#include <QDebug>
// ...
void MyWidget::someSlot() {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Test", "Quit?",
QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes) {
qDebug() << "Yes was clicked";
QApplication::quit();
} else {
qDebug() << "Yes was *not* clicked";
}
}
Should work on Qt 4 and 5, requires QT += widgets on Qt 5, and CONFIG += console on Win32 to see qDebug() output.
See the StandardButton enum to get a list of buttons you can use; the function returns the button that was clicked. You can set a default button with an extra argument (Qt "chooses a suitable default automatically" if you don't or specify QMessageBox::NoButton).
You can use the QMessage object to create a Message Box then add buttons :
QMessageBox msgBox;
msgBox.setWindowTitle("title");
msgBox.setText("Question");
msgBox.setStandardButtons(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
if(msgBox.exec() == QMessageBox::Yes){
// do something
}else {
// do something else
}
QT can be as simple as that of Windows. The equivalent code is
if (QMessageBox::Yes == QMessageBox(QMessageBox::Information, "title", "Question", QMessageBox::Yes|QMessageBox::No).exec())
{
}
I'm missing the translation call tr in the answers.
One of the simplest solutions, which allows for later internationalization:
if (QMessageBox::Yes == QMessageBox::question(this,
tr("title"),
tr("Message/Question")))
{
// do stuff
}
It is generally a good Qt habit to put code-level Strings within a tr("Your String") call.
(QMessagebox as above works within any QWidget method)
EDIT:
you can use QMesssageBox outside a QWidget context, see #TobySpeight's answer.
If you're even outside a QObject context, replace tr with qApp->translate("context", "String") - you'll need to #include <QApplication>
QMessageBox includes static methods to quickly ask such questions:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char **argv)
{
QApplication app{argc, argv};
while (QMessageBox::question(nullptr,
qApp->translate("my_app", "Test"),
qApp->translate("my_app", "Are you sure you want to quit?"),
QMessageBox::Yes|QMessageBox::No)
!= QMessageBox::Yes)
// ask again
;
}
If your needs are more complex than provided for by the static methods, you should construct a new QMessageBox object, and call its exec() method to show it in its own event loop and obtain the pressed button identifier. For example, we might want to make "No" be the default answer:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char **argv)
{
QApplication app{argc, argv};
auto question = new QMessageBox(QMessageBox::Question,
qApp->translate("my_app", "Test"),
qApp->translate("my_app", "Are you sure you want to quit?"),
QMessageBox::Yes|QMessageBox::No,
nullptr);
question->setDefaultButton(QMessageBox::No);
while (question->exec() != QMessageBox::Yes)
// ask again
;
}
If you need asynchronous call you should use open and result methods instead of question or exec. Sample code inside a QWidget method:
QMessageBox* const message = new QMessageBox(QMessageBox::Icon::Question, tr("Test"),
tr("Quit?"), QMessageBox::Button::Yes | QMessageBox::Button::No, this);
message->setDefaultButton(QMessageBox::Button::No);
message->open();
connect(message, &QDialog::finished, this, [message] {
message->deleteLater();
if (message->result() == QMessageBox::Button::Yes) {
QApplication::quit();
}
});
It should not be usefull just for a quit dialog but for other confirmation dialogs where parent widget might be destroyed by external events it is the main way to avoid a crash.
Python equivalent code for a QMessageBox which consist of a question in it and Yes and No button. When Yes Button is clicked it will pop up another message box saying yes is clicked and same for No button also. You can push your own code after if block.
button_reply = QMessageBox.question(self,"Test", "Are you sure want to quit??", QMessageBox.Yes,QMessageBox.No,)
if button_reply == QMessageBox.Yes:
QMessageBox.information(self, "Test", "Yes Button Was Clicked")
else :
QMessageBox.information(self, "Test", "No Button Was Clicked")
If you want to make it in python you need check this code in your workbench.
also write like this.
we created a popup box with python.
msgBox = QMessageBox()
msgBox.setText("The document has been modified.")
msgBox.setInformativeText("Do you want to save your changes?")
msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
msgBox.setDefaultButton(QMessageBox.Save)
ret = msgBox.exec_()
I want to be able to stop a user from moving a QSplitter at runtime. Calling setEnabled(false) does this, but it also disables all child widgets - which isn't what I want. Is there a way to achieve this? Do I have to disable the splitter, and then manually re-enable all child widgets? That seems rather cumbersome, for something that must be a reasonably common practise.
Can anyone suggest anything?
Do this:
for (int i = 0; i < splitter->count(); i++)
{
QSplitterHandle *hndl = splitter->handle(i);
hndl->setEnabled(false);
}
Actually, I've never seen anyone ever disable a splitter: They are there so the user can layout the UI as she needs, so why would anyone want to disable this? Either you need a splitter or you can use one of the normal layouts (which the user can't resize).
If you still want to try, I think you should look at closestLegalPosition() or getRange(). If you just return the width of the widget, then resizing should stop working.
You have to do two things. Set the widgets (that shouldn't be resizeable) inside the splitter to FixedSize and change the cursor of the correspondent splitter handles to Qt::ArrowCursor. The handles start with zero (left and not used), so the first handle between two widgets is by index 1.
Here's a sample (put the code in main.cpp):
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.resize(800, 300);
window.setWindowTitle("Splitter Test");
window.show();
QSplitter *splitter = new QSplitter(&window);
QListView *listview = new QListView;
QTreeView *treeview = new QTreeView;
QTextEdit *textedit = new QTextEdit;
splitter->addWidget(listview);
splitter->addWidget(treeview);
splitter->addWidget(textedit);
splitter->setChildrenCollapsible(false);
splitter->show();
listview->show();
treeview->show();
textedit->show();
//make the lisview 'fix'
listview->setFixedSize(listview->width(), listview->height());
//change the cursor over the splitter handle between listview and
//treeview to ArrowCursor
splitter->handle(1)->setCursor(Qt::ArrowCursor);;
return app.exec();
}
Now the first splitter handle is disabled and the second works.