I am trying to publish my first application for N9 on Ovi Store but I have failed the QA:
SUMMARY: The application cannot back to front via clicking application icon after suspending the application in background.
STEPS TO REPRODUCE:
1. Download and install the content.
2. Launch the application.
3. Suspend the application in background.
4. Click the application icon.
ACTUAL RESULT: There is no response when clicking the application icon.
EXPECTED RESULT: The application backs to front. Features and controls in the content file must function as specified.
My application is written in C++ with Qt, there is one QWidget.
In the main function I am calling
Widget w;
w.showFullScreen();
I also override eventFilter
bool Widget::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::ActivationChange && m_created) {
if (isActiveWindow()) {
resume();
}
else {
pause();
}
}
return QWidget::eventFilter(object, event);
}
But when reproducing the bug, I do not get to eventFilter at all. What should I do to get back to my app, when I press the icon?
The correct way to do this is to use applauncherd to launch your app by calling invoker. You could also use its booster feature to improve the startup time of your app (by using MDeclarativeCache).
Here is the documentation on how to use invoker:
http://harmattan-dev.nokia.com/docs/platform-api-reference/showdoc.php?pkn=applauncherd-user-documentation&wb=daily-docs&url=Li94bWwvZGFpbHktZG9jcy9hcHBsYXVuY2hlcmQtdXNlci1kb2N1bWVudGF0aW9u
And here's how to use MDeclarativeCache:
http://harmattan-dev.nokia.com/docs/platform-api-reference/showdoc.php?pkn=applauncherd-mdeclarativecache-documentation&wb=daily-docs&url=Li94bWwvZGFpbHktZG9jcy9hcHBsYXVuY2hlcmQtbWRlY2xhcmF0aXZlY2FjaGUtZG9jdW1lbnRhdGlvbg%3D%3D
After some experiments, I've found the solution. It's a bit weird, but it works:
bool Widget::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::ActivationChange)
{
if (isActiveWindow())
{
resume();
}
else
{
pause();
}
}
else if (event->type() == QEvent::SockAct) // pure magic
{
activateWindow();
resume();
}
return QWidget::eventFilter(object, event);
}
After pressing the icon on the home screen, filetr QEvent::SockAct and restore application.
Related
I'm working on determining if a certain touchscreen will be compatible with an application and recently got a loaner model of an Elo 2402L touchscreen. I've installed the driver the company provides and was able to see multi-touch events using the evtest utility (parser for /dev/input/eventX).
The thing is that I'm running Scientific Linux 6.4, which uses Linux kernel 2.6.32. I've seen a lot of mixed information on touchscreen compatibility for Linux kernels before 3.x.x. Elo says that their driver only supports single-touch for 2.6.32. Also, I've seen people say that the majority of the compatibility issues with touch events in this kernel version are with Xorg interfaces.
I developed a very simple Qt5 application to test whether Qt could detect the touch events or not, because I'm not sure whether Qt applications are X-based and if they read events directly from /dev/input or something else.
However, despite a simple mouse event handler being able to correctly register mouse events, I also created a simple touch event handler and nothing happens when I touch the main screen. There is a beep, as part of the driver that Elo provides makes a beep when the screen is touched, so I know that SOMETHING is registering that touch, but neither the desktop, nor this application seem to recognize the touch event.
Also, yes, the WA_AcceptTouchEvents attribute is set to true in the window's constructor.
I have a simple mainwindow.h:
...
protected:
int touchEvent(QTouchEvent *ev);
...
And mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) {
...
setAttribute(Qt::WA_AcceptTouchEvents, true);
touchPoints = 0;
}
...
int MainWindow::touchEvent(QTouchEvent *ev) {
switch(ev->type()) {
case QEvent::TouchBegin:
touchPoints++;
break;
case QEvent::TouchEnd:
touchPoints--;
break;
}
ui->statusBar->showMessage("Touch Points: " + touchPoints);
}
Is there something wrong with the way I'm using the touch event handler? Or is there some issue with the device itself? Does Qt read input events directly from /dev/input, or does it get its input events from X?
Very confused here, as I haven't used Qt before and want to narrow down the cause before I say that it's the device causing the issue.
Also, if anyone has any insight into the device / kernel compatibility issue, that would be extremely helpful.
The QTouchEvent documentation says:
Touch events occur when pressing, releasing, or moving one or more
touch points on a touch device (such as a touch-screen or track-pad).
To receive touch events, widgets have to have the
Qt::WA_AcceptTouchEvents attribute set and graphics items need to have
the acceptTouchEvents attribute set to true.
Probably you just need to call setAttribute(Qt::WA_AcceptTouchEvents, true) inside the MainWindow constructor.
Is there something wrong with the way I'm using the touch event handler?
There is no touch event handler. If you change:
int touchEvent(QTouchEvent *ev);
to:
int touchEvent(QTouchEvent *ev) override;
(which you should always do when you are trying to override virtual functions so you can catch exactly this kind of mistake), you'll see that there is no such function for you to override. What you need to override is the event() handler:
protected:
bool event(QEvent *ev) override;
You need to check for touch events there:
bool MainWindow::event(QEvent *ev)
{
switch(ev->type()) {
case QEvent::TouchBegin:
touchPoints++;
break;
case QEvent::TouchEnd:
touchPoints++;
break;
default:
return QMainWindow(ev);
}
ui->statusBar->showMessage("Touch Points: " + touchPoints);
}
However, it might be better to work with gestures instead of touch events. But I don't know what kind of application you're writing. If you wanted to let Qt recognize gestures rather than implementing them yourself through touch events, you would first grab the gestures you want, in this case pinching:
setAttribute(Qt::WA_AcceptTouchEvents);
grabGesture(Qt::PinchGesture);
and then handle it:
bool MainWindow::event(QEvent *ev)
{
if (e->type() != QEvent::Gesture) {
return QMainWindow::event(e);
}
auto* gestEv = static_cast<QGestureEvent*>(e);
if (auto* gest = gestEv->gesture(Qt::PinchGesture)) {
auto* pinchGest = static_cast<QPinchGesture*>(gest);
auto sf = pinchGest->scaleFactor();
// You could use the pinch scale factor here to zoom an image
// for example.
e->accept();
return true;
}
return QMainWindow::event(e);
}
Working with gestures instead of touch events has the advantage of using the platform's gesture recognition facilities, like those of Android and iOS. But again, I don't know what kind of application you're writing and on what kind of platform you're working on.
I am using QSlider element in my app as a toogle switch. I took this project over from a colleage of mine who left. So far so good, except, that this switch has a black label around it. I can't really change the style and rework everything so, I am stuck with label and slider as switch.
I need to handle events from button clicked and button released. I do it like this-
bool CustomSlider::eventFilter(QObject *obj, QEvent *ev )
{
if (ev->type() == QEvent::MouseButtonRelease)
{
changeSliderValue();
}
if(ev->type() == QEvent::MouseButtonPress){
lastSaved = ui->horizontalSlider->value();
}
return QWidget::eventFilter(obj,ev);
And changeSliderValue()-
void CustomSlider::changeSliderValue()
{
int current = ui->horizontalSlider->value();
if(lastSaved==current){
if(current){
ui->horizontalSlider->setValue(0);
}else{
ui->horizontalSlider->setValue(1);
}
}
}
So, in short, if I click, it saves state of slider and when releasing it checks against saved state. I do it because QSlider has button pressed signal as well. I have put debug messages in my code and funny thing is debug messages fire. So my code works. If I call ui->horizontalSlider->value() I get the correct(changed) value But the slider is not moving. I have tried checking sequences, if it does not change value twice(it does not), I have tried calling repaint() and so far nothing works. Except if don't minimize(via minimize button) or click in different app(like web browser) and then I can see that slider moves to correct postion. I am completely confused. Has anyone encoutered something like this before?
So, I have an application where if a particular button is kept pressed it plays an audio device, when the button is released it stops the audio device. I use keyPressEvent and KeyReleaseEvent to implement this which is similar to the code below:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 pressed"<<endl;
}
else
{
QWidget::keyPressEvent(event);
}
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 released"<<endl;
}
else
{
QWidget::keyReleaseEvent(event);
}
}
}
But apparently isAutoRepeat function isn't working as I can see continuous print out of key_0 pressed and key_0 released despite the fact I haven't released the 0 key after I have pressed it. Is my code wrong or something else is wrong?
Thanks.
EDIT
I think this is happening because the MainWindow loses the keyboard focus. How can I actually find out which widget has the focus? I'm actually using some widgets when Qt::Key_0 pressed, but I thought I set all those possible widgets to Qt::NoFocus, I guess it's not working.
I'm trying to know which widget has the focus by doing the following:
QWidget * wigdet = QApplication::activeWindow();
qDebug()<<wigdet->accessibleName()<<endl;
but it always prints an empty string. How can I make it print the name of the widget which has the keyboard focus?
So as I also stumbled over this issue (and grabKeyboard didn't really help), I begun digging in qtbase. It is connected to X11 via xcb, and by default, in case of repeated keys, X11 sends for each repeated key a release-event immediately followed by a key-press-event. So holding down a key results in a sequence of XCB_BUTTON_RELEASE/XCB_BUTTON_PRESS-events beeing sent to the client (try it out with xev or the source at the end of this page).
Then, qt (qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp) tries to figure out from these events whether its an autorepeat case: when a release is received, it uses a lookahead feature to figure if its followed by a press (with timestamps close enough), and if so it assumes autorepeat.
This does not always work, at least not on all platforms. For my case (old and outworn slow laptop (Intel® Celeron(R) CPU N2830 # 2.16GHz × 2) running ubuntu 16.04), it helped to just put a usleep (500) before that check, allowing the press event following the release event to arrive... it's around line 1525 of qxcbkeyboard.cpp:
// look ahead for auto-repeat
KeyChecker checker(source->xcb_window(), code, time, state);
usleep(500); // Added, 100 is to small, 200 is ok (for me)
xcb_generic_event_t *event = connection()->checkEvent(checker);
if (event) {
...
Filed this as QTBUG-57335.
Nb: The behaviour of X can be changed by using
Display *dpy=...;
Bool result;
XkbSetDetectableAutoRepeat (dpy, true, &result);
Then it wont send this release-press-sequences in case of a hold down key, but using it would require more changes to the autorepeat-detection-logic.
Anyway solved it.
The problem was that I have a widget which is a subclass of QGLWidget which I use to show some augmented reality images from Kinect. This widget takes over the keyboard focus whenever a keyboard button is pressed.
To solve this problem, I needed to call grabKeyboard function from the MainWindow class (MainWindow is a subclass of QMainWindow), so this->grabKeyboard() is the line I needed to add when key_0 button is pressed so that MainWindow doesn't lose the keyboard focus, and then when the key is released I needed to add the line this->releaseKeyboard() to resume normal behaviour, that is, other widgets can have the keyboard focus.
I am using the same Qt program (v5.2.1) on both windows 7 and Ubuntu. In both I have a QMainWindow that overrides the event function like so:
bool MyMainWindow::event(QEvent *event) {
switch(event->type()) {
case QEvent::Move:
// Do stuff
break;
}
return QWidget::event(event);
}
In there I look for the QEvent::Move to do some processing. On windows, if I click and drag the window around the event seems to happen continuously. However, on Ubuntu it only happens after the mouse is released. I can't seem to find this in any documentation and would appreciate some clarification.
Single-clicking an application icon in the Dock launches that application or, if the application is already open, switches you to that application and brings forward all open windows in that application.
In my Qt app, I do not see this behavior. I have a dock shortcut, I minimize the app on dock, and if I click the minimized instance the app is restored.
If I click the application icon in the Dock (dock shortcut), nothing happens.
(I can right-click, and "Show All Windows" - but that is not similar to the behavior of all other apps)
I tried to place the code from this answer: https://stackoverflow.com/a/15363738/1217150
Placing qDebug statements,
if(test) qDebug("registered");
in the constructor, and
void MyApplictionClass::onClickOnDock()
{
qDebug("dock clicked");
}
On start-up I get both messages... but after minimizing, or any time after start-up, there is no reaction to dock shortcut being clicked or double clicked.
Full code insertion:
MyApplicationClass.h:
#include <objc/objc.h>
#include <objc/message.h>
class MyApplicationClass: public QApplication
{
....
bool dockClickHandler(id self, SEL _cmd, ...);
void onClickOnDock();
};
MyApplicationClass.cpp:
MyApplicationClass::MyApplicationClass()
{
....
objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls, sharedApplication);
if(appInst != NULL)
{
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
objc_object* delClass = objc_msgSend(delegate, sel_registerName("class"));
const char* tst = class_getName(delClass->isa);
bool test = class_addMethod((objc_class*)delClass,
sel_registerName("applicationShouldHandleReopen::hasVisibleWindows:"),
(IMP)dockClickHandler(appInst, sharedApplication), "B#:");
if(!test) qDebug("not registered");
else qDebug("registered");
}
.....
}
bool MyApplicationClass::dockClickHandler(id self, SEL _cmd, ...)
{
Q_UNUSED(self)
Q_UNUSED(_cmd)
onClickOnDock();
return true;
}
void MyApplicationClass::onClickOnDock()
{
qDebug("dock clicked");
}
Perhaps I did something wrong ? There are a few items I don't understand... The referred post had dockClickHandler not part of the class... I just don't know where they have it... And I had to give it arguments when I registered it, otherwise it refused to build. Still, it seems to do... something... just not the right thing. In that code, I do not see where the app will react to clicking the dock.
Is this the correct approach ? Or how can I get my app to restore when its dock shortcut is clicked ?
(OSX 10.6-10.9, Qt 4.8)
If you are refering to this: QT on OS X, how to detect clicking the app Dock Icon, the onClickOnDock should not be a member of the class.
You CAN put it as a normal function inside the MyApplicationClass.cpp file.
There is a hack at least for Qt 5.9.1, when you can implement onClickOnDock() without platform specific code at all. See https://stackoverflow.com/a/46488514/8695355