Qt 4.8. Tablet Events on Mac OS 10.6.8 - qt

TabletEvents comes as mouse events.
Actual for MAC OS Qt 4.8.0 - 4.8.5.
Works fine in Qt 4.7.3 on any OS and Qt 4.8.0 on Windows and Linux.
I have two instances of QGraphcisScene and two instances of QGraphicsView.
The same types, but one view have a parent, and the another - doesn't (also it's transparent, used for drawing something over desktop).
I'm using tablet (wacom pen and touch) for painting. I handle QTabletEvents and it works only for QGrahicsView instance that doesn't have parent (means parent==0).
On the view with parent (
QMainWindow->centralWidget->ControlContainerWidget->QStackedLayout->QGraphicsView
) tablet events doesn't comes. They comes to QApplication::eventFilter fine, but doesn't comes to view. They comes to QMainWindow as mouseEvents.
If i set parent to 0, tablet events delivers fine.
The 1st receiver of tablet event is QMainWindow.
I see that inside qt_mac_handleTabletEvent:
QWidget *qwidget = [theView qt_qwidget];
QWidget *widgetToGetMouse = qwidget;
And then:
`qt_sendSpontaneousEvent(widgetToGetMouse, &qtabletEvent);`
qtabletEvent - is not accepted event created just before calling sendSpontaneousEvent.
Then inside QApplication::notify():
QWidget *w = static_cast<QWidget *>(receiver);
QTabletEvent *tablet = static_cast<QTabletEvent*>(e);
QPoint relpos = tablet->pos();
bool eventAccepted = tablet->isAccepted();
while (w) {
QTabletEvent te(tablet->type(), relpos, tablet->globalPos(),
tablet->hiResGlobalPos(), tablet->device(), tablet->pointerType(),
tablet->pressure(), tablet->xTilt(), tablet->yTilt(),
tablet->tangentialPressure(), tablet->rotation(), tablet->z(),
tablet->modifiers(), tablet->uniqueId());
te.spont = e->spontaneous();
res = d->notify_helper(w, w == receiver ? tablet : &te);
eventAccepted = ((w == receiver) ? tablet : &te)->isAccepted();
e->spont = false;
if ((res && eventAccepted)
|| w->isWindow()
|| w->testAttribute(Qt::WA_NoMousePropagation))
break;
relpos += w->pos();
w = w->parentWidget();
}
tablet->setAccepted(eventAccepted);
As we can see:
res = d->notify_helper(w, w == receiver ? tablet : &te);
It calls event processing by filters, layouts and then - QMainWindow::tabletEvent. Default implementation is event->ignore().
Since QMainWindow have no Parent, it is all.
So tablet event doesn't comes to QMainWindow childs.
Then seems it is QWidget *qwidget = [theView qt_qwidget]; works wrong.
Unfortunately, i can't debug it...
Please give me some hints... i'm stucked...

I spent more time on comparison Qt 4.8.0 and 4.7.3 and now i see that it is the problem in internal qt event dispatcher. It sends event to NSWindow (QMainWindow) instead of NSView (QGraphicsView).
I didn't found where is the problem, but i found that QMainWindow returns false from ::event() method.
So i reimplemented that method and parsed tablet event there:
bool UBMainWindow::event(QEvent *event)
{
bool bRes = QMainWindow::event(event);
if (NULL != UBApplication::boardController)
{
UBBoardView *controlV = UBApplication::boardController->controlView();
if (controlV && controlV->isVisible())
{
switch (event->type())
{
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
{
return controlV->directTabletEvent(event);
}
}
}
}
return bRes;
}
The problem is: i need to use tablet for any controls in application, so i need to determine when QGraphicsView is under mouse cursor:
bool UBBoardView::directTabletEvent(QEvent *event)
{
QTabletEvent *tEvent = static_cast<QTabletEvent *>(event);
tEvent = new QTabletEvent(tEvent->type()
, mapFromGlobal(tEvent->pos())
, tEvent->globalPos()
, tEvent->hiResGlobalPos()
, tEvent->device()
, tEvent->pointerType()
, tEvent->pressure()
, tEvent->xTilt()
, tEvent->yTilt()
, tEvent->tangentialPressure()
, tEvent->rotation()
, tEvent->z()
, tEvent->modifiers()
, tEvent->uniqueId());
if (geometry().contains(tEvent->pos()))
{
if (NULL == widgetForTabletEvent(this->parentWidget(), tEvent->pos()))
{
tabletEvent(tEvent);
return true;
}
}
return false;
}
Also i need to stop handle tablet events for QGraphicsView childs.
QWidget *UBBoardView::widgetForTabletEvent(QWidget *w, const QPoint &pos)
{
Q_ASSERT(w);
UBBoardView *board = qobject_cast<UBBoardView *>(w);
QWidget *childAtPos = NULL;
QList<QObject *> childs = w->children();
foreach(QObject *child, childs)
{
QWidget *childWidget = qobject_cast<QWidget *>(child);
if (childWidget)
{
if (childWidget->isVisible() && childWidget->geometry().contains(pos))
{
QWidget *lastChild = widgetForTabletEvent(childWidget, pos);
if (board && board->viewport() == lastChild)
continue;
if (NULL != lastChild)
childAtPos = lastChild;
else
childAtPos = childWidget;
break;
}
else
childAtPos = NULL;
}
}
return childAtPos;
}
Maybe when i will have more time - i will investigate qt more deeply and fix that problem at all.

Related

Qt text editor project: auto-indenting when Enter is pressed after an opening curly brace

I'm working on a text editor project in Qt, and I've added the following method override to a class that's of type QPlainTextEdit:
/* Custom handler for events. Used to handle the case of Enter being pressed after an opening brace.
*/
bool Editor::eventFilter(QObject* obj, QEvent* event)
{
bool isKeyPress = event->type() == QEvent::KeyPress;
if(isKeyPress)
{
QKeyEvent *key = static_cast<QKeyEvent*>(event);
if(key->key() == Qt::Key_Enter || key->key() == Qt::Key_Return)
{
QString documentContents = document()->toPlainText();
if(documentContents.length() >= 1)
{
int indexToLeftOfCursor = textCursor().position() - 1;
if(indexToLeftOfCursor >= 0 && indexToLeftOfCursor < documentContents.length())
{
bool hitEnterAfterOpeningBrace = documentContents.at(indexToLeftOfCursor) == '{';
if(hitEnterAfterOpeningBrace)
{
// TODO determine indentation level of the opening brace
insertPlainText("\n\t\n}");
QTextCursor cursor = textCursor();
cursor.setPosition(cursor.position() - 3);
setTextCursor(cursor);
}
}
}
}
else
{
return QObject::eventFilter(obj, event);
}
}
else
{
return QObject::eventFilter(obj, event);
}
return false;
}
Without the code for setting the text cursor's position, the result looks like this:
The cursor ends up on line 4, and there's a tab on line 2. My intent is to move the cursor so it sits to the right of the tab, so I tried to do that with the code I've written. But that gives me this:
Here, the indent is still on line 2, but now there's an extra line between the tab and the closing brace, which is a little odd.
Even more strange is that this happens regardless of what new position I enter. I tried doing cursor.position() - 2 and cursor.position() - 1 out of curiosity, for example, but I still got the same result.
What am I misunderstanding here about inserting the text and moving the cursor?

How can I get access to a QMessageBox by QTest

I am creating some automated GUI tests in my application using QTest.
I can access the widgets from my application using the command:
savePushButton = mainWindow->findChild<QPushButton *>("savePushButton");
It is working fine, but now I have to click on the OK button of a QMessageBox.
I created the QMessageBox in my application like this:
if( something_wrong )
{
QMessageBox::warning(new Widget(), "Title", "Something wrong!");
}
How can I have access to this QMessageBox, and its buttons?
I found a solution on the following link: http://www.qtcentre.org/threads/31239-Testing-modal-dialogs-with-QTestLib .
It uses the command QApplication::topLevelWidgets(); to get a widget list. Then it searches for the message box widget and simulates a key enter (QTest::keyClick(mb, Qt::Key_Enter);) which closes the message box.
Example:
void MyTest::testDialog()
{
QTimer::singleShot(500, this, SLOT(timeOut()));
QVERIFY(functionThatProducesMessageBox());
}
void MyTest::timeOut()
{
QWidgetList allToplevelWidgets = QApplication::topLevelWidgets();
foreach (QWidget *w, allToplevelWidgets) {
if (w->inherits("QMessageBox")) {
QMessageBox *mb = qobject_cast<QMessageBox *>(w);
QTest::keyClick(mb, Qt::Key_Enter);
}
}
}
The header file must contain the Q_OBJECT macro to use the signals and slots mechanism.
Example:
class MyClass: public QWidget
{
Q_OBJECT
public:
...
It worked well for me since the UI (thread) is blocked when the message box appears.
Note: remember to rebuild the project when you add the Q_OBJECT macro.
It often helps to look to Qt's auto tests:
void ExecCloseHelper::timerEvent(QTimerEvent *te)
{
if (te->timerId() != m_timerId)
return;
QWidget *modalWidget = QApplication::activeModalWidget();
if (!m_testCandidate && modalWidget)
m_testCandidate = modalWidget;
if (m_testCandidate && m_testCandidate == modalWidget) {
if (m_key == CloseWindow) {
m_testCandidate->close();
} else {
QKeyEvent *ke = new QKeyEvent(QEvent::KeyPress, m_key, Qt::NoModifier);
QCoreApplication::postEvent(m_testCandidate, ke);
}
m_testCandidate = Q_NULLPTR;
killTimer(m_timerId);
m_timerId = m_key = 0;
}
}
Judging from that code, you can get the message box via QApplication::activeModalWidget(). Testing native (I'm assuming they're native) widgets is difficult, which is likely why they chose to send key events, as you don't need to know e.g. the location of the buttons for those, as you would with a mouse click.

QtInputContextFactory returning NULL on embedded target

On my embedded system I don't have X11, Mac, Win, S60, etc. I keep getting a NULL ( 0 ) pointer returned from the create method of the QInputContextFactory class. I verified that QT_NO_LIBRARY is not defined.
On my Desktop Qt Build this works just fine.
I also verified that my custom key and parent are being passed to the method.
What could cause this to fail? -->
if (QInputContextFactoryInterface *factory =
qobject_cast<QInputContextFactoryInterface*>(loader()->instance(key))) {
result = factory->create(key);
}
Here is the entire method:
QInputContext *QInputContextFactory::create( const QString& key, QObject *parent )
{
QInputContext *result = 0;
#if defined(Q_WS_X11) && !defined(QT_NO_XIM)
if (key == QLatin1String("xim")) {
result = new QXIMInputContext;
}
#endif
#if defined(Q_WS_WIN)
if (key == QLatin1String("win")) {
result = new QWinInputContext;
}
#endif
#if defined(Q_WS_MAC)
if (key == QLatin1String("mac")) {
result = new QMacInputContext;
}
#endif
#if defined(Q_WS_S60)
if (key == QLatin1String("coefep")) {
result = new QCoeFepInputContext;
}
#endif
#ifdef QT_NO_LIBRARY
Q_UNUSED(key);
#else
qDebug() << "Here we are";
if (QInputContextFactoryInterface *factory =
qobject_cast<QInputContextFactoryInterface*>(loader()->instance(key))) {
result = factory->create(key);
}
#endif
if (result)
result->setParent(parent);
return result;
}
Within Qt, the QInputContextFactory class is front-end on loading input context plug-ins. It will fail to load an input context plug-in if it fails to exist, or hasn't been deployed properly. Input context plug-ins are typically stored under $QT_PLUGIN_PATH/inputmethods. As such, if there is no plug-in within that directory, the create method of the QInputContextFactory will return NULL.
Of note, Qt does provide a few mechanisms for customizing the location of plug-ins. Refer to the following for more detail on this:
http://qt-project.org/doc/qt-4.8/deployment-plugins.html

QlineEdit with some default text for which cursor should not be moved?

In QT, a created lineEdit shows a text using the setText() method.
But the cursor is movable for the default text. I want the cursor should not be movable for the default text.
My lineEdit type has been set as password. Hence the default text('Password') is also displayed as '********'. Whenever user types the type has to be changed as password and when there is no text or until the user have not typed any text, the lineEdit should display the plain text 'password'
Any idea to fix the above two issues?
In the constructor put
ui->lineEdit->setPlaceholderText("password");
ui->lineEdit->setReadOnly(1);
And in on_lineEdit_selectionChanged() SLOT, put
ui->lineEdit->setText("");
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit->setReadOnly(0);
I noticed this question has tag pyqt so I'll put an actual answer related to that tag for those actually looking for a python way instead of c++.
self.searchEditText = QtGui.QLineEdit()
self.searchEditText.setPlaceholderText("Search for word")
I managed to do what you want by deriving a class from QLineEdit as per following..
Constructor..
QCustomLineEdit::QCustomLineEdit(QWidget *parent) :
QLineEdit(parent)
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(onTextChanged(QString)));
connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int)));
setEchoMode(QLineEdit::Password); // Echo mode in your case..
m_echoMode = echoMode(); // Member variable to store original echo mode..
m_placeHolderText = "Password"; // Member variable..
m_isPlaceHolderActive = true; // Member varible..
// Default case..
setPlaceholderText("");
setStyleSheet("QCustomLineEdit{color: gray;}");
setEchoMode(QLineEdit::Normal);
setText(__placeHolderText);
}
Override keyPressEvent..
void QCustomLineEdit::keyPressEvent(QKeyEvent *e)
{
if(m_isPlaceHolderActive)
{
if(e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
e->accept();
else
QLineEdit::keyPressEvent(e);
return;
}
QLineEdit::keyPressEvent(e);
}
Cursor position change event..
void QCustomLineEdit::onCursorPositionChanged(int /*oldPos*/, int newPos)
{
if(m_isPlaceHolderActive)
{
if(newPos != 0)
setCursorPosition(0);
}
}
Text change event..
void QCustomLineEdit::onTextChanged(const QString &text)
{
if(m_isPlaceHolderActive)
{
if(text.compare(m_placeHolderText) != 0)
{
m_isPlaceHolderActive = false;
// Remove the 'placeHolderText' from 'text' itself..
QString temp = text;
temp = temp.mid(0, text.lastIndexOf(m_placeHolderText));
setStyleSheet("QCustomLineEdit{color: black;}");
setEchoMode(m_echoMode);
setText(temp);
}
else
{
setEchoMode(QLineEdit::Normal);
setText(m_placeHolderText);
setStyleSheet("QCustomLineEdit{color: gray;}");
setCursorPosition(0);
}
}
else
{
if(text.isEmpty())
{
m_isPlaceHolderActive = true;
setStyleSheet("QCustomLineEdit{color: gray;}");
setEchoMode(QLineEdit::Normal);
setText(m_placeHolderText);
}
}
}
I have written it very hastily to just show you. Test it yourself and feel free to point any mistake(s) or optimization(s). Hope this helps.
For question 1, in Qt 5.0 and higher, setPlaceholderText does what you want. https://codereview.qt-project.org/#change,45326

Qt, QTouchEvents not send to QGraphicsItem

I manually create QTouchEvents and send them to a QGraphicsView's viewport(). While the QGraphicsScene does get the events, my QGraphicsItems (or ...Objects) don't. setAcceptTouchEvents is set to true:
myObject::myObject(QGraphicsItem * parent)
: QGraphicsItem(parent)
{
setAcceptTouchEvents(true);
}
...
bool myObject::sceneEvent( QEvent * event )
{
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
//this is never reached?!
return true;
}
return QGraphicsItem::sceneEvent(even);
}
//in a different class:
QTouchEvent * event = new QTouchEvent(t,QTouchEvent::TouchScreen,Qt::NoModifier,states,tpList);
if(m_view->viewport())
{
qApp->postEvent(m_view->viewport(), event);
}
Also, I get "QGestureManager::deliverEvent: could not find the target for gesture" warnings, though I don't even try to grab QGestures anywhere in my program.

Resources