Qt and dead keys in a custom widget - c++

Issue
I'm unable to make dead keys work in my Qt program, while on the same system Qt applications (konsole or kmail for instance) are correctly processing them.
How to reproduce
testcase.pro
TEMPLATE = app
TARGET = testcase
INCLUDEPATH += .
QT += core widgets gui
HEADERS += testcase.hpp
SOURCES += testcase.cpp
testcase.hpp
#include <QWidget>
class TestWindow: public QWidget
{
Q_OBJECT
public:
TestWindow(QWidget* parent=0, Qt::WindowFlags flags=0);
void keyPressEvent(QKeyEvent* event);
};
testcase.cpp
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QKeyEvent>
#include "testcase.hpp"
TestWindow::TestWindow(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
setAttribute(Qt::WA_KeyCompression);
}
void TestWindow::keyPressEvent(QKeyEvent* event)
{
qDebug() << event;
}
int main(int argc, char** argv)
{
QApplication app(argc, argv);
TestWindow mainWin;
mainWin.show();
return app.exec();
}
Compile the above program (qmake; make), launch it. Dead keys give for instance:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 45, 0, ""e"", false, 1)
I was expecting
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
This would also be acceptable:
QKeyEvent(KeyPress, 1001252, 0, ""^"", false, 1)
QKeyEvent(KeyPress, 234, 0, ""ê"", false, 1)
What I've tried
I'm using a Ubuntu 14.10 system with locale fr_FR.UTF-8
I've tried
with Qt 5.3.0 and Qt 4.8.6 as provided on the system.
unseting XMODIFIERS (the default value is #im=ibus is being reported an issue by some)
changing the locale (again, google find reports were the part after the dot is an issue, I've tried the 4 variants UTF-8, utf-8, UTF8 and utf8)
with and without the setAttribute(Qt::WA_KeyCompression); in the constructor.
None changed my observable behavior.
Searching the web show mainly (only?) system related issues. As written above, I've tried the proposed solution, that doesn't solve my problem and the fact that other Qt applications I've tried are able to process the dead key makes me think I miss something in my code, especially that with a slightly more complex example, I'm able to use dead keys with Qt provided widgets (for instance a QLineEdit).

This is a partial answer, but as nobody answered that could be useful for someone later.
Dead keys are handled by input methods which are also handling the compose key and the way Chinese characters and others may be entered.
The widget must signify that it handles input method:
setAttribute(Qt::WA_InputMethodEnabled, true);
Then it has to overwrite two virtual members:
void inputMethodEvent(QInputMethodEvent*);
QVariant inputMethodQuery(Qt::InputMethodQuery) const;
To handle the dead keys and compose, it seems to be enough to have
void TestWindow::inputMethodEvent(QInputMethodEvent* event)
{
if (!event->commitString().isEmpty()) {
QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier,
event->commitString());
keyPressEvent(&keyEvent);
}
event->accept();
}
QVariant TestWindow::inputMethodQuery(Qt::InputMethodQuery) const
{
return QVariant();
}
but that's were my answer is incomplete:
I'm not sure it is really enough even for that
I'm sure it is not enough for more complex writing system and I lack the prerequisite to understand the documentation I've found.

Related

QT 5.6.1 app crashes after exiting slot only on x86 arch but not x64

I am facing an issue that i cannot understand and i need your lights. I have found no answers on my problem on others topics yet.
Context :
I am using QT 5.6.1 compiled dynamically (x86 and x64) on a Windows 10 x64.
Problem :
I have made a basic program compiled twice (x86 and x64) that just display a Window with a button and connects the button to the "clicked" signal. My Window is correctly displayed but when i hit my button to fire signal the app crashed just after exiting the SLOT connected to the SIGNAL (qDebug is correctly called from the slot). But i am only facing this issue with x86 QT dll's...
Here is the code:
.cpp:
MyWidget::MyWidget()
{
QMainWindow *pqMainWindow= new QMainWindow(this);
QPushButton *pqButton= new QPushButton("MyButton");
/* Setting up the window */
pqMainWindow->setWindowModality(Qt::WindowModal);
pqMainWindow->setGeometry(geometry());
pqMainWindow->move(QPoint(100, 100));
/* Connecting signal clicked to slot */
QObject::connect(pqButton, SIGNAL(clicked(bool)), this, SLOT(_onMyActionTriggered(bool)));
pqMainWindow->setCentralWidget(pqButton);
/* Showing the window */
pqMainWindow->show();
}
MyWidget::~MyWidget()
{
/* Nothing to do yet */
}
void MyWidget::_onMyActionTriggered(bool bValue)
{
qDebug("Slot <_onMyActionTriggered> called");
}
int __cdecl main(int argc, char **argv)
{
QApplication qapp(argc, argv);
MyWidget widget;
return qapp.exec();
}
.h
class MyWidget: public QWidget
{
Q_OBJECT;
public:
MyWidget();
virtual ~MyWidget();
private slots:
void _onMyActionTriggered(bool bValue);
private:
};
Here is the call trace :
Qt5Widgets!QAction::activate+0x103
Qt5Widgets!QToolButton::nextCheckState+0x1a
Qt5Widgets!QAbstractButton::click+0x103
Qt5Widgets!QAbstractButton::mouseReleaseEvent+0x7e
Qt5Widgets!QToolButton::mouseReleaseEvent+0xd
Qt5Widgets!QWidget::event+0xa8
Qt5Widgets!QSizePolicy::QSizePolicy+0x83b
Qt5Core!QCoreApplication::translate+0x30f56
Qt5Gui!QGuiApplicationPrivate::processMouseEvent+0x6c1
USER32!SetManipulationInputTarget+0x53
USER32!DispatchMessageW+0x251
USER32!DispatchMessageW+0x10
qwindows!qt_plugin_query_metadata+0x2065
Qt5Core!QCoreApplication::exec+0x160
qt_auth_test!wmain+0x7c
qt_auth_test!QObject::event+0xb5
KERNEL32!BaseThreadInitThunk+0x24
ntdll!RtlUnicodeStringToInteger+0x21e
ntdll!RtlCaptureContext+0xe1
I have compiled QT myself but i have the same result when using dll and lib downloaded on QT Website.
Thanks everyone for your help, i've finally found the issue.
Here is the answer :
My program uses the stdcall (/Gz) convention but QT uses cdecl (/Gd). The generated moc file was compiled with the stdcall convention and this is what caused the issue (stack problem when releasing parameters because of different convention between callee and caller).
Now my program is compiled using the stdcall convention (because it is mandatory for me) BUT the generated moc files are compiled with cdecl and i have set the __cdecl keyword on my private slots headers! Now It works perfectly!
Once again, thank for your time. I hope this topic will help someone one day with a similar issue.
Setting window modality on a child of a hidden widget makes no sense. Perhaps that's a problem. Try the following, and check if/when it crashes. There definitely is a Qt 5.6 bug on OS X at least: when the button's modality is reverted, the widget still doesn't receive input and you must exit the application by other means.
#include <QtWidgets>
int main(int argc, char **argv)
{
int n = 0;
QApplication app{argc, argv};
QPushButton widget{"Widget"};
QPushButton button{"Button", &widget};
button.setWindowFlags(Qt::Window);
QObject::connect(&button, &QPushButton::clicked, [&]{
button.setText(button.text().append("."));
++n;
if (n == 5) {
button.setWindowModality(Qt::WindowModal);
button.hide();
button.show();
}
else if (n == 10) {
button.setWindowModality(Qt::NonModal);
button.hide();
button.show();
}
else if (n == 15)
app.quit();
});
button.move(widget.geometry().bottomRight());
widget.show();
button.show();
return app.exec();
}

How to detect global key sequence press in Qt?

I want to detect whether a key sequence is pressed and want to perform certain task on that event in Qt. Currently I can detect keypresses for certain widgets but how to detect global keypresses. By global I mean that even if the application is minimized or hidden , it should detect key press.
I tried making an eventFilter for the application, by first overloading QObject::eventFilter like this:
bool GlobalEventFilter::eventFilter(QObject *Object, QEvent *Event)
{
if (Event->type() == QEvent::KeyPress)
{
QKeyEvent *KeyEvent = (QKeyEvent*)Event;
switch(KeyEvent->key())
{
case Qt::Key_F1:
cout<<"F1 press detected"<<endl;
return true;
default:
break;
}
}
return QObject::eventFilter(Object,Event);
}
and then installing that object as the eventFilter for my application:
QApplication a(argc,argv);
a.installEventFilter(new GlobalEventFilter());
and I also tried the doing this:
QCoreApplication::instance()->installEventFilter(new GlobalEventFilter());
In both the cases I am able to detect key presses when my application window is open but it fails when window is minimized or hidden. How to solve this?
See the documentation of QKeyEvent:
Key events are sent to the widget with keyboard input focus when keys are pressed or released.
That means, if you wish to use QKeyEvent, you need to have the keyboard focus. Filtering those events does not change this conceptually either. I am not sure where you got the filtering idea from.
You might want to look into alternative solutions, for instance how it is implemented in kwin, etc. In general, be careful with this use case. It could do weird things without the end users noticing it.
You could take a look at this class:
QxtGlobalShortcut Class Reference
You could write something like this then:
main.cpp
#include <QxtGlobalShortcut>
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
class MyGlobalShortcutHandler : public QObject
{
Q_OBJECT
public:
explicit MyGlobalShortcutHandler(QObject *parent = 0)
: QObject(parent)
{
m_globalShortcut.setShortcut(QKeySequence("Ctrl+Shift+X"));
m_globalShortcut.setEnabled(true);
connect(&m_globalShortcut, SIGNAL(activated()), SLOT(handleGlobalShortcut()));
}
public slots:
void handleGlobalShortcut()
{
qDebug() << "Global shortcut handled";
}
private:
QxtGlobalShortcut m_globalShortcut;
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
QMainWindow mainWindow;
MyGlobalShortcutHandler myGlobalShortcut();
mainWindow.show();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += qxt
QXT = core gui
SOURCES += main.cpp
Build and Run
qmake-qt4 && make && ./main
This code should be working with Qt 4, at least. It prints out the debug statement for me. You could easily verify this on your end.
To get the libqxt library, please visit the following link:
http://dev.libqxt.org/libqxt/wiki/Home
This feature is not implemented in Qt. You can use Qxt. Qxt is an extension library for Qt providing a suite of cross-platform utility classes to add functionality not readily available in Qt. It has Global Shortcut (hot keys) which detects key presses even if the application is minimized.
After compiling and linking your application to Qxt, you can include QxtGlobalShortcut:
#include <QxtGlobalShortcut>
Example usage:
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));

Using Qt's signals in a non Qt application (C++)

I have a lot of existing code using Qt, and more specifically Qt signals and slots to time specific actions.
Now I need to use this code within a new application which is not a Qt application (and cannot be - I am writing a plugin to visual studio). Anyway - how can I get the existing code to actually intercept the signals and activate the relevant slots?
Do I need to somehow create a dummy Qt application? If so - how do I cause it to process the signals without becoming a blocking loop to the rest of my code?
It seems that if you write something like this, the "Test" message is still printed even though there is no event loop, so this could be a clue:
#include <QObject>
#include <QCoreApplication>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent) : QObject(parent) {}
void testMethod() { emit testSignal(); }
signals:
void testSignal();
public slots:
void testSlot() { qDebug() << "Test"; }
};
#include "main.moc"
int main(int argc, char **argv)
{
// QCoreApplication coreApplication(argc, argv);
MyClass myObject(0);
QObject::connect(&myObject, SIGNAL(testSignal()), &myObject, SLOT(testSlot()));
myObject.testMethod();
// return coreApplication.exec();
return 0;
}
This way, you would still need Qt, but you could avoid having a "blocking" event loop. However, it might be simpler to just rearrange the code from the signal-slot layering to direct calls, depending on how many direct calls you would need to do for a signal emitted.
This is a common problem when using ASIO and Qt together. The solution I have found is to make a Broker object, and run the Qt and ASIO loops on their own threads. The Broker is the target for emit calls to the ASIO message queue, and the emitter to the Qt message queue. Take care of the syncronisation/data copying in the Broker.

Q_ENUMS are "undefined" in QML?

Enums are not working out for me.
I have registered them with Q_ENUMS()
I did not forget the Q_OBJECT macro
the type is registered using qmlRegisterType()
the module is imported in QML
In short, everything is "by-the-book" but for some reason I continue getting undefined for each and every enum in QML. Am I missing something?
class UI : public QQuickItem {
Q_OBJECT
Q_ENUMS(ObjectType)
public:
enum ObjectType {
_Root = 0,
_Block
};
...
};
...
qmlRegisterType<UI>("Nodes", 1, 0, "UI");
...
import Nodes 1.0
...
console.log(UI._Root) // undefined
EDIT: Also note that the registered enums are indeed available to use for the metasystem, for some reason they do not work in QML.
UPDATE: I just found this bug: https://bugreports.qt.io/browse/QTBUG-33248
But unlike that bug my root component is a bare UI not a custom element with UI as its root.
Turns out that it is actually possible to use enum values form QML in console.log(), the following code is actually working.
class A : public QObject {
Q_OBJECT
Q_ENUMS(EA)
public:
enum EA {
EA_NULL = 0,
EA_ONE
};
};
class B : public A {
Q_OBJECT
Q_ENUMS(EB)
public:
enum EB {
EA_TWO = 2,
EA_THREE
};
};
#include "main.moc"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<A>("test", 1, 0, "A");
qmlRegisterType<B>("test", 1, 0, "B");
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/enums/main.qml"));
viewer.showExpanded();
return app.exec();
}
and...
Component.onCompleted: {
console.log(A.EA_NULL)
console.log(A.EA_ONE)
console.log(B.EA_NULL)
console.log(B.EA_ONE)
console.log(B.EA_TWO)
console.log(B.EA_THREE)
}
Output is:
0
1
0
1
2
3
So I guess there is another problem besides "you are not using it correctly"... It might have to do with the bug I mentioned above, and the fact that when I instantiate the UI element, I actually instantiate a QML component which is a tree of objects with the UI as the root. While this doesn't prove to be any problem for working with pointers from C++ with the full QML objects, it does seem to mess enums for some reason.
Your problem is not the exposure of the enum, but the fact that you have a leading underscore. Once you remove that, it will work.
You need to start the enum value with uppercase letter. Some rule is necessary to distinguish enums from attached properties from enums. Leading uppercase will refer to enums, and the rest for attached properties (or undefined if not set).
Admittedly, there is also a warning in Qt itself because if you try to assign that enum value to an int or var property, you are currently not getting a warning, and having discussed that issue a little bit with the current maintainer, it seems to be a bug which will be fixed later on.
See the working code below with the correspondigly proposed solution:
main.cpp
#include <QQuickView>
#include <QQuickItem>
#include <QGuiApplication>
#include <QUrl>
class UI : public QQuickItem {
Q_OBJECT
Q_ENUMS(ObjectType)
public:
enum ObjectType {
Root = 0,
_Block
};
};
#include "main.moc"
int main(int argc, char **argv)
{
QGuiApplication guiApplication(argc, argv);
qmlRegisterType<UI>("Nodes", 1, 0, "UI");
QQuickView *view = new QQuickView;
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
return guiApplication.exec();
}
main.qml
import Nodes 1.0
import QtQuick 2.0
Rectangle {
id: button
width: 500; height: 500
MouseArea {
anchors.fill: parent
onClicked: console.log(UI.Root)
}
}
main.pro
TEMPLATE = app
TARGET = main
QT += quick
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
0
I had the exact same problem, and thanks to these solutions I figured out my problem. However, in case anyone else is dealing with the same mixup, here it is a little clearer.
I was using a class from C++ in QML just like here, so I had something like this in main.cpp
qmlRegisterType<EnumClass>("Enums", 1, 0, "EnumClass");
and in the .qml
import Enums 1.0
EnumClass {
id: ec
}
and tried and tried to access ec.SomeEnum but kept getting undefined even though, the QtCreator autocomplete said ec.SomeEnum should exist.
This simply does not work, and to get this to work I had to use
EnumClass.SomeEnum
instead (just like they do here).

Why doesn't my QDeclarativeItem get any mouse/keyboard events?

This is a reduced down, minimum complete example demonstrating my problem:
I have an application which hosts a QDeclarativeView; file events.cpp:
#include <QApplication>
#include <QDeclarativeView>
#include "TestItem.h"
int main(int argc,char* argv[]) {
QApplication app(argc,argv);
qmlRegisterType<TestItem>("Testing",1,0,"Tester");
QDeclarativeView page;
page.setSource(QUrl("page.qml"));
Q_ASSERT(page.status()==QDeclarativeView::Ready);
page.show();
return app.exec();
}
That TestItem is a subclassed QDeclarativeItem defined in file TestItem.h:
#ifndef _TestItem_h_
#define _TestItem_h_
#include <iostream>
#include <QDeclarativeItem>
#include <QPainter>
class TestItem : public QDeclarativeItem {
Q_OBJECT
public:
TestItem() {
setFlag(QGraphicsItem::ItemHasNoContents,false);
std::cerr << "[TestItem created]";
}
void paint(QPainter* painter,const QStyleOptionGraphicsItem*,QWidget*) {
painter->drawLine(0,0,width(),height());
painter->drawLine(0,height(),width(),0);
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*) {
std::cerr << "[TestItem::mousePressEvent]";
}
void keyPressEvent(QKeyEvent*) {
std::cerr << "[TestItem::keyPressEvent]";
}
};
#endif
and the page.qml file loaded into the QDeclarativeView is simply:
import QtQuick 1.0
import Testing 1.0
Tester {
width: 200
height: 200
}
It's all built (with Qt 4.8 on Debian-Wheezy amd64) with qmake file
CONFIG += debug
QT += core gui declarative
TARGET = events
TEMPLATE = app
SOURCES += events.cpp
HEADERS += TestItem.h
and, once built, when I run ./events I get a window displaying the Tester's painted 'X', as expected:
and a [TestItem created] logged to the console. However, clicking within the window or pressing keys completely fails to invoke either of the mouse or key event handlers.
I'm completely mystified. Is some extra magic (in the C++ or QML domains) needed to get mouse/keyboard events routed to these sort of "plugin" QDeclarativeItem classes? I certainly don't have any problems defining a MouseArea in the QML file and having it do stuff to QML state, and the code this is reduced from has no problems with signals and slots interoperating between the C++ item and QML code... but when it comes to mouse/keyboard events, there's just no sign of them on the C++ side.
To get (left) mouse events, all that is needed is to add
setAcceptedMouseButtons(Qt::LeftButton);
in the TestItem constructor. This is a little surprising as the documentation for the inherited QGraphicsItem::setAcceptedMouseButtons says "By default, all mouse buttons are accepted", but maybe something else in the setup messes with the state.
To get keyboard events, setFocus(true) just needs to be invoked. The documentation seems to imply setFlag(QGraphicsItem::ItemIsFocusable,true) should also be called, but it doesn't actually seem to be necessary in my test.