Qt, QTouchEvents not send to QGraphicsItem - qt

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.

Related

How to use a JavaFX binding to disable a button if TextField has invalid input

I got the following code from The Definitive Guide to Modern Java Clients with JavaFX:
updateButton.disableProperty()
.bind(listView.getSelectionModel().selectedItemProperty().isNull()
.or(wordTextField.textProperty().isEmpty())
.or(definitionTextArea.textProperty().isEmpty()));
I would like to modify it so the button is disabled if the String entered into frequencyTextField is not a nonnegative integer. I added a term to the conjunction as shown:
updateButton.disableProperty()
.bind(listView.getSelectionModel().selectedItemProperty().isNull()
.or(isLegalFrequency(frequencyTextField.textProperty()).not())
.or(wordTextField.textProperty().isEmpty())
.or(definitionTextArea.textProperty().isEmpty()));
Although it is probably not relevant, here is the method that tests validity:
private BooleanProperty isLegalFrequency(StringProperty sp) {
System.out.println("isLegalFrequency(" + sp.get() + ")");
try {
int value = Integer.parseInt(sp.get());
return new SimpleBooleanProperty(value >= 0);
} catch (NumberFormatException e) {
return new SimpleBooleanProperty(false);
}
}
My problem is that the button is always disabled. I have established that isLegalFrequency() is called only once, when the scene is being created. This makes sense, since I am passing frequencyTextField.textProperty(), not calling a method on it (which presumably sets up a listener behind the scenes).
Is there a way to modify the program without adding an explicit listener so it behaves as I'd like, or is it necessary to create a ChangeListener on frequencyTextField.textProperty?
Very generally, you can create an arbitrary method:
private Boolean validate() {
// arbitrary implementation here...
// in your case something like
if (listView.getSelectionModel().getSelectedItem() == null) return false ;
if (wordTextField.getText().isEmpty()) return false ;
if (definitionTextArea.getText().isEmpty()) return false ;
if (! isLegalFrequency(frequencyTextField.getText())) return false ;
return true ;
}
and then do
updateButton.disableProperty().bind(Bindings.createBooleanBinding(
() -> ! validate(),
listView.getSelectionModel().selectedItemProperty(),
frequencyTextField.textProperty(),
wordTextField.textProperty(),
definitionTextArea.textProperty()));
The parameters to the createBooleanBinding() method are a Callable<Boolean> (i.e. a method taking no parameters and returning a Boolean) followed by zero or more instances of javafx.beans.Observable (any property or ObservableList, etc, will work). You should include any property (or other observable) here that should trigger a recalculation of the disableProperty() when it changes.

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.

How to draw dash instead of null in QSpinBox?

I need to draw dash instead of null in QSpinBox. Also I need to make dash key pressing equalling null key pressing.
How can I do this?
You can use setSpecialValueText();
QSpinBox spinBox;
spinBox->setSpecialValueText(tr("-"));
You can then check if the special value is selected by connecting valueChanged(QString) function. Note that this is different from valueChanged(int) You can then check the value of the passed string in a slot, and if it is equal to special text, you can do something.
main()
{
connect(spinBox, SIGNAL(valueChanged(QString)), this, SLOT(doSomething(QString)));
}
void doSomething(QString valueStr)
{
if(valueStr == spinBox->specialValueText())
// Do something
else
//Convert valueStr to int and do other stuff
}
Or you could do something like this:
main()
{
connect(spinBox, SIGNAL(valueChanged()), this, SLOT(doSomething()));
}
void doSomething()
{
if(spinBox->value() == 0)
// Do something with dash
else
//Do something with the value
}
For your other question, you need to create a keyPressEvent and check if pressed key is dash or not. If it's dash you can call another function to do something. Edit: BTW, the index of specialValueText() is 0.
Edit: Or you can create a QShortcut in your main function.
new QShortcut(QKeySequence(Qt::Key_Minus), this, SLOT(doSomething()));
Edit continued: doSomething() is a slot function. Put, for example void doSomething(); in the private slots: section of your header file. And in the cpp file define a function similar to this:
void MainWindow::doSomething()
{
ui->spinBox->setValue(0);
//This is the slot called when you press dash.
}
Edit still continued:
You need to declare a protected: function in the header like this:
virtual void keyPressEvent(QKeyEvent *event);
Then you need to define this function in your cpp file. Like this:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Minus)
ui->spinBox->setValue(0);
}
You don't have to connect any signals or slots for this function. It's an event.
That means when dash is pressed ui->spinBox->setValue(0);
Because of that, you need to create a spinBox with a range starting from 0.
spinBox->setRange(0, 100);
That means,
if(spinBox->value() == 0)
//Then specialValueText is selected.

How to Break an Infinite Loop using QPushButton?

I have an infinite loop, inside the loop I want to insert a state whenever I click a button, it will break the current loop.
I've tried several ways like:
if(ui->btnStop->isDown())
{
break;
}
if(ui->btnStop->isChecked())
{
break;
}
and
if(cv::waitKey(10)>=0)
{
break;
}
But, it doesn't work.
I wonder why cv::waitKey doesn't work in Qt, but in a non-Qt project it will work flawlessly.
Are there any other way to break an infinite loop with a QPushButton?
Any help would be appreciated.
It doesn't work because the event processor cannot run whilst execution is locked in your loop. The easiest solution is to simply call QApplication::processEvents() in each loop, this will force the event processor to run.
// Add a boolean to your class, and a slot to set it.
MyClass
{
...
private slots:
void killLoop() { killLoopFlag_ = true; }
private:
bool killLoopFlag_;
}
// In the constructor, connect the button to the slot.
connect( ui->btnStop, SIGNAL( clicked() ),
this, SLOT( killLoop ) );
// Then when performing the loop, force events to be processed and then
// check the flag state.
killLoopFlag_ = false;
while ( true ) {
// ...Do some stuff.
QApplication::processEvents();
if ( killLoopFlag_ ) {
break;
}
}
However you need to ask yourself: Should I be doing long running calculations inside the GUI thread? The answer is usually no.

Qt 4.8. Tablet Events on Mac OS 10.6.8

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.

Resources