I have looked at the 5 Qt testing examples including the one about GUI events, but these examples are way too simple.
I want to test my program by launching it, simulating some clicks, and checking the value of instance variables that have been changed by those clicks.
I assume that this test below is illogical: a.exec() blocks the thread until the program is closed, and when the program is closed w has been deleted I think (or will be deleted later?).
So how to write system/GUI tests?
My test:
void LaunchProgramTest::LaunchProgramTestFunction() {
QApplication a(argc, argv);
MainWindow *w = new MainWindow();
w->show();
a.exec();
int testResult = w->myTestFunction();
qDebug() << testResult; //Prints big numbers like "-17891602" or "1753770528" as if testResult was not initialized
QVERIFY2(testResult == 3, "Incorrectly changed");
}
In mainWindow.h I declared a variable:
int testValue;
Mainwindow.cpp is the class for the main GUI of the program. In the constructor I added
testValue = 2;
Then in a function that is executed upon events I wrote
void MainWindow::on_actionTest_clicked() {
testValue = 3;
}
enter code hereSo)) you need to add QTest, add .pro
QT += testlib
and
#include <QTest>
I will show an example of my implementation for MousePress, the rest you can do yourself))
struct EventMK
{
int type;
QString m_widPath;
int _timer;
int width;
int height;
QPoint p;
QPoint g;
int button;
int buttons;
int modifiers;
int _key;
QString text;
void print(){
qDebug()<<"{ \n"
<<"type "<< type<<","<<"\n"
<<"Widget_Path "<< m_widPath<<","<<"\n"
<<"Timer "<< _timer<<","<<"\n"
<<"Width "<< width<<","<<"\n"
<<"Height "<< height<<","<<"\n"
<<"Pos_x "<< p.x()<<","<<"\n"
<<"Pos_y "<< p.y()<<","<<"\n"
<<"Global_x "<< g.x()<<","<<"\n"
<<"Global_y "<< g.y()<<","<<"\n"
<<"Button "<< button<<","<<"\n"
<<"Buttons "<< buttons<<","<<"\n"
<<"Modifiers "<< modifiers<<","<<"\n"
<<"Key "<< _key<<","<<"\n"
<<"Text "<< text<<"\n"
<<"}\n";
}
};
QWidget * _getWidget(EventMK ev)
{
QString wname = ev.m_widPath;
QStringList wpath = wname.split ( "/" );
return QWidgetUtils::getAWidget(&wpath);
}
void _postExecution(EventMK ev, QWidget *widget)
{
if (widget){
//set focus
QWidgetUtils::setFocusOnWidget(widget);
//end simulation
widget->setUpdatesEnabled ( true );
widget->update();
}
}
QPoint adaptedPosition(EventMK ev, QWidget *w)
{
if (w == nullptr)
return QPoint(ev.p.x(), ev.p.y());
int orig_w = ev.width;
int orig_h = ev.height;
int curr_w = w->width();
int curr_h = w->height();
int new_x = ev.p.x() * curr_w / orig_w;
int new_y = ev.p.y() * curr_h / orig_h;
return QPoint(new_x, new_y);
}
and function implementation
void executeMousePressEvent(EventMK ev)
{
QWidget* widget = _getWidget(ev);
if ( widget == nullptr )
{
qDebug()<<"error: "<<__LINE__<<__FUNCTION__;
return;
}
// _preExecutionWithMouseMove(ev, widget);
if (widget){
QTest::mousePress ( widget, (Qt::MouseButton)ev.button ,
(Qt::KeyboardModifier) ev.modifiers,
adaptedPosition(ev,widget));
}
_postExecution(ev, widget);
}
now left to fill struct EventMK , you need to populate it from MouseButtonPress events.
Here is my example
bool eventFilter(QObject *obj, QEvent *event)
{
///
/// process control
///
//window events
if (event->type() == QEvent::KeyPress)
{
handleKeyPressEvent(obj, event);
}
//mouse events
else if (event->type() == QEvent::MouseButtonPress)
{
handleMousePressEvent(obj, event);
}
else if (event->type() == QEvent::MouseButtonRelease)
{
handleMouseReleaseEvent(obj, event);
}
else if (event->type() == QEvent::MouseButtonDblClick)
{
handleMouseDoubleEvent(obj, event);
}
else if (event->type() == QEvent::Wheel)
{
handleWheelEvent(obj, event);
}
//keyboard events
else if (event->type() == QEvent::Close)
{
handleCloseEvent(obj, event);
}
///the event should continue to reach its goal...
return false;
}
and
void handleMousePressEvent(QObject *obj, QEvent *event)
{
QWidget *widget = isValidWidget(obj);
if (!widget){
return;
}
QMouseEvent *me = dynamic_cast< QMouseEvent*> ( event );
//create the event
if (widget != nullptr){
EventMK evkm;
evkm.type = QOE_MOUSE_PRESS; // set your type
evkm._timer = _timer.restart(); // add your QElapsedTimer
evkm.m_widPath = QWidgetUtils::getWidgetPath(widget);
evkm. width = widget->width();
evkm. height = widget->height();
QPoint p ( me->pos() );
QPoint g = widget->mapToGlobal ( p );
evkm. p = p;
evkm. g = g;
evkm. button = me->button();
evkm. buttons = me->buttons();
evkm. modifiers = me->modifiers();
evkm.print();
}
//send event if EventMK is valid
}
so, it turns out we can write a scenario and run what you wanted, thanks
I grabGesture()ed one of my buttons:
buttons[0]->grabGesture(Qt::TapAndHoldGesture);
in the constructor, and declared:
bool event(QEvent *event);
in protected slots, and implemented it like this:
bool MyClass::event(QEvent *event)
{
if (event->type() == QEvent::Gesture){
QGestureEvent *gestevent = static_cast<QGestureEvent *>(event);
if (QGesture *gest = gestevent->gesture(Qt::TapAndHoldGesture)){
QTapAndHoldGesture *tapgest = static_cast<QTapAndHoldGesture *>(gestevent->gesture(Qt::TapAndHoldGesture));
cout << "grabbed a gesture event" << endl;
}
return true;
}
cout << "not a gesture event" << endl;
return QWidget::event(event);
}
and I keep getting "not a gesture event" printed to screen however I press (normal press / long press / ... )
What I'm trying to do is a long key press (from the keyboard)
It's said in the Qt Documentation:
A gesture could be a particular movement of a mouse, a touch screen
action, or a series of events from some other source. The nature of
the input, the interpretation of the gesture and the action taken are
the choice of the developer.
So I suppose also a keyboard can trigger QGesture events.
If the class handling the grap (MyClass) event is not the class where the gesture is detected on (QPushButton assuming buttons[0] is a QPushButton), then you need and event filter:
buttons[0]->grabGesture(Qt::TapAndHoldGesture);
buttons[0]->installEventFilter( myClass ); // myClass being a MyClass instance
Now, myClass object will be forwarded all events from buttons[0], this is done using QObject::eventFilter virtual function:
bool MyClass::eventFilter(QObject *obj, QEvent *event)
{
if ( event->type() == QEvent::Gesture && obj == buttons[0] )
{
QGestureEvent *gestevent = static_cast<QGestureEvent *>(event);
if (QGesture *gest = gestevent->gesture(Qt::TapAndHoldGesture)){
QTapAndHoldGesture *tapgest = static_cast<QTapAndHoldGesture *>(gestevent->gesture(Qt::TapAndHoldGesture));
cout << "grabbed a gesture event" << endl;
return true;
}
}
// standard event processing
return Parent::eventFilter(obj, event); // Parent being MyClass parent type, maybe QDialog or QWidget
}
I am following this instructions to get the child events. My aim is to change the value of QSpinBox by dragging the mouse up/down by keeping the Ctrl key pressed (like in GIMP).
However, while this works for the window, QSpinBox ignores the drag and just selects the contents inside.
I have installed the eventfilter into all childs with:
eventFilter(QObject *obj, QEvent *event)
{
switch(event->type())
{
case QEvent::ChildAdded:
{
QChildEvent* ce = static_cast<QChildEvent*>(event);
// Install the filter to each new child object created
ce->child()->installEventFilter(this);
break;
}
case QEvent::ChildRemoved:
{
QChildEvent* ce = static_cast<QChildEvent*>(event);
// Remove the the filter from each new child object removed
ce->child()->removeEventFilter(this);
break;
}
case QEvent::MouseButtonPress:
{
QMouseEvent* me = static_cast<QMouseEvent*>(event);
qDebug() << QString::number(me->pos().x());
qDebug() << QString::number(me->pos().y());
return true;
}
case QEvent::MouseButtonRelease:
{
QMouseEvent* me = static_cast<QMouseEvent*>(event);
qDebug() << QString::number(me->pos().x());
qDebug() << QString::number(me->pos().y());
return true;
}
case QEvent::MouseMove:
{
QMouseEvent* me = static_cast<QMouseEvent*>(event);
qDebug() << QString::number(me->pos().x());
qDebug() << QString::number(me->pos().y());
return true;
}
}
return QWidget::eventFilter(obj, event);
}
and in the constructor using:
setMouseTracking(true);
installEventFilter(this);
How can I fully make QSpinBox ignore selection?
Please can some-one suggest on this question..? I have to send data from my TX thread to mainwindow using class derived from QEvent.
I want my RX thread to throw an event whenever some data is received so hat i can display that data in the Mainwindow.
I start a main thread when button start is pressed. I save the pointer to my mainwindow inside the object of main thread. I will use this pointer to post events to the mainwindow object.
When first time i enter the Qthread function dowork_rx(). Event is thrown & i am able to catch it in customEvent() handler. But when dowork_rx() while loop starts when i throws the event it does not trigger the customEvent() function.
Please suggest how to resolve this problem.
My qevent class :---
//String event derived class
template <typename T> class StringEvent : public QEvent
{
QString m_str;
public:
explicit StringEvent(const QString val) : QEvent(staticType()), m_str(val)
{
}
void setvalue(QString val)
{
m_str = val;
}
QString value() const
{
return m_str;
}
static QEvent::Type staticType()
{
static int type = QEvent::registerEventType();
return static_cast<QEvent::Type>(type);
/*
static int type;
if(type == 0)
{
type = QEvent::registerEventType();
}
return static_cast<QEvent::Type>(type);*/
}
static bool is(const QEvent * ev)
{
return ev->type() == staticType();
}
};
class UpdateEvent : public StringEvent<UpdateEvent>
{
public:
explicit UpdateEvent(QString val): StringEvent(val)
{
//qDebug() << "hello";
}
};
class ClearEvent : public StringEvent<ClearEvent>
{
public:
explicit ClearEvent(QString val): StringEvent(val)
{
}
};
Dowork function of the thread :----
// Common slot for the rx - thread
void RxThreadObject::dowork_rx()
{
int i =0;
qDebug() << "\nrx start \n";
myUpdateEvent_rx = new UpdateEvent("UpdateEventObject - dowork_rx");
//myUpdateEvent_rx->setvalue("first");
QCoreApplication::postEvent(m_pMainThreadObj->ptrmainwindow, myUpdateEvent_rx);
qDebug() << "\nrx throw event - done \n";
while(!m_bQuitRx)
{
SleepTimerDelay::sleep(2);
if(i==0){
//qDebug() << "\nrx throw event - done 11 \n";
myUpdateEvent_rx = new UpdateEvent("first");
myUpdateEvent_rx->setvalue("first");
QCoreApplication::postEvent(m_pMainThreadObj->ptrmainwindow, myUpdateEvent_rx);
i++;
}else{
//qDebug() << "\nrx throw event - done 22 \n";
myUpdateEvent_rx = new UpdateEvent("second");
myUpdateEvent_rx->setvalue("second");
QCoreApplication::postEvent(m_pMainThreadObj->ptrmainwindow, myUpdateEvent_rx);
i=0;
}
}
qDebug() << "\nrx end \n";
}
Event handler :----
/*!
** Custom event handler
*/
void MainWindow::customEvent(QEvent *event)
{
qDebug() << "oo customEvent";
if (UpdateEvent::is(event)) {
UpdateEvent *tempUpdateEvent = static_cast<UpdateEvent *>(event);
qDebug() << tempUpdateEvent->value();
}
else if (ClearEvent::is(event)) {
ClearEvent *tempClearEvent = static_cast<ClearEvent *>(event);
qDebug() << tempClearEvent->value();
}
}
/*!
** event filter handler
*/
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
qDebug() << "oo eventFilter";
if (UpdateEvent::is(event)) {
UpdateEvent *tempUpdateEvent = static_cast<UpdateEvent *>(event);
qDebug() << tempUpdateEvent->value();
}
else if (ClearEvent::is(event)) {
ClearEvent *tempClearEvent = static_cast<ClearEvent *>(event);
qDebug() << tempClearEvent->value();
}
return true;
}
I have implemented QGraphicsObject with QTouchEvents and reimplemented the sceneEvent function.
bool LynxItem::sceneEvent(QEvent *event)
{
//qDebug() << "LynxItem::sceneEvent:" << itemId;
switch (event->type()) {
case QEvent::Gesture:
{
qDebug() << "LynxItem::sceneEvent:Gesture" << itemId;
return gestureEvent(static_cast<QGestureEvent*>(event));
break;
}
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
{
qDebug() << "LynxItem::sceneEvent:TouchUpdate" << itemId;
QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
/* This doesn't allow two objects to move at the same time */
//setPos ( touchPoint1.scenePos() - touchPoint1.pos());
/* This does work but the item is always moved from top left */
setPos(touchPoint1.scenePos());
event->accept();
break;
}
default:
return QGraphicsItem::sceneEvent(event);;
}
return true;
}
My problem is that when I touch the item the items top right corner comes to the touch point. I want to offset the point inside where I touched. However when I do that I can only move one item at a time.
Ok, to answer my own question:
setPos ( touchPoint1.scenePos() - touchPoint1.pos());
Is incorrect. On the TouchBegin I should store touchPoint1.pos():
m_TouchOffset = touchPoint1.pos();
Then use that first position instead
setPos ( touchPoint1.scenePos() - m_TouchOffset);