How to simulate properly an long click on the mouse on a button?! (Updated with full code)



  • Hi I have been trying to simulate an long click on a custom QPushButton, so if its just an click to do something, if not to do another think and is working +/-.

    My problem is when the window opens from the long click and I keep pressing and move the mouse, its like I'm clicking again and simulates just a click on the button and I would like to prenvent this from hapening but without success.

    Here is the code of the button when is created witht the connection

    @
    // To create the buttons dynamic
    void MainWindow::designKeyWidget(QWidget *widgetDesign, int widgetWith, int widgetHeight, int marginTop, int marginLeft,
    int marginLeftButton, int numRow, int numCollum)
    {
    myQPushButton *Buttons;
    int buttonWidth = (widgetWith - (marginLeftButton * numCollum))/ numCollum;
    int buttonHeight = (widgetHeight - (marginLeftButton * numRow)) / numRow;
    for (int i = 0; i < numRow; i++)
    {
    for (int y = 0; y < numCollum; y++)
    {
    Buttons = new myQPushButton(widgetDesign);
    Buttons->setGeometry(marginLeft,marginTop,buttonWidth,buttonHeight);
    Buttons->show();
    connect(Buttons,SIGNAL(pressed()),this,SLOT(KeyTimerListener()));
    connect(Buttons,SIGNAL(released()),this,SLOT(KeyClickListener()));
    marginLeft = buttonWidth + marginLeft + marginLeftButton;
    }
    marginLeft = 0;
    marginTop = marginTop + buttonHeight + marginLeftButton;
    }
    }

    // The listener thats run when the button is released
    void MainWindow::KeyClickListener()
    {
    myQPushButton *button = (myQPushButton *)sender();
    int opc = button->get_Tipo();
    switch (opc)
    {
    case 0:
    if ( this->timer->isActive() )
    {
    this->timer->stop();
    this->timer->deleteLater();
    delete this->timer;
    }

    break;
        case 1:
            changeGroupPage(button->get_Pagina(), GVars.getGroupMapButton());
            fillItemWidget(button->get_Pagina(), ui->ItemWidget);
            break;
        case 2:
            GVars.setSelectedGroup(button->get_id());
            fillItemWidget(button->get_Pagina(), ui->ItemWidget);
            break;
        case 3:
            fillGroupWidget(button->get_Pagina(), ui->GroupWidget, GVars.getGroupMapButton());
            break;
        case 4:
            //exit(0);
            break;
        default:
            break;
    }
    

    }

    // When the button is clicked
    void MainWindow::KeyTimerListener()
    {
    myQPushButton *button = (myQPushButton *)sender();
    int opc = button->get_Tipo();
    switch (opc)
    {
    case 0:
    this->timer = new QTimer(this);
    this->timer->setSingleShot(true);
    this->ItemToChange = button->get_id();
    connect(this->timer, SIGNAL(timeout()), this, SLOT(ItemConfigListener()));
    this->timer->start(2000);
    break;
    default:
    break;
    }
    }

    // this is called when the timeout is called
    void MainWindow::ItemConfigListener()
    {
    this->timer->stop();
    this->timer->deleteLater();
    delete this->timer;
    Form = new Config();
    Form->setItem(idbConnect.search_Item(this->ItemToChange));
    }
    @

    So when the button is pressed it will start the time, and if it reachs the 2 seconds it will start the function ConfigListener. This will open a new window with some configuration, but like I have sayed if the left mouse button is still being pressed when this window pop up and move the mouse ( still pressing the button ) it will simulate an click and to the part that stops the timer. Where I think its strange, should the timeout stop the timer it self?! Why does he simulate an click?|



  • First of all, i don't see the code that destroys the QTimer, you keep creating a new timer every click but you don't destroy the previous one.
    Second, QTimer::start() will keep firing timeout() every 2 seconds, you either have to explicitly stop the timer, or you have to QTimer::setSingleShot(true).
    Maybe there are other problems related to how an OS responds to moving the mouse out from the button while the mouse is still pressed, but that's something you'll need to investigate after you first fix the two problems above.



  • The last code on the OP, is the one on release, so if the button is release before the 2 seconds it will stop the timer for it, this wasn't a problem.

    If needed I will put a more complete code where for bether understanding of what I'm doing.

    EDIT:

    Now the timer is deleting properly when it goes into the long click, but the problem remains about the second click that it does virtualy ( since I only do 1 click ) when I move the mouse when still have the left button down.



  • well, do you tread differently the button release depending on whether or not the timer already fired? maybe this is what happens: the timer fires, and then when you release the button it also goes to your button-release code. dunno, just guessing, you're not providing sufficient code.



  • Ok I will provide the full code for this part:

    @
    // To create the buttons dynamic
    void MainWindow::designKeyWidget(QWidget *widgetDesign, int widgetWith, int widgetHeight, int marginTop, int marginLeft,
    int marginLeftButton, int numRow, int numCollum)
    {
    myQPushButton *Buttons;
    int buttonWidth = (widgetWith - (marginLeftButton * numCollum))/ numCollum;
    int buttonHeight = (widgetHeight - (marginLeftButton * numRow)) / numRow;
    for (int i = 0; i < numRow; i++)
    {
    for (int y = 0; y < numCollum; y++)
    {
    Buttons = new myQPushButton(widgetDesign);
    Buttons->setGeometry(marginLeft,marginTop,buttonWidth,buttonHeight);
    Buttons->show();
    connect(Buttons,SIGNAL(pressed()),this,SLOT(KeyTimerListener()));
    connect(Buttons,SIGNAL(released()),this,SLOT(KeyClickListener()));
    marginLeft = buttonWidth + marginLeft + marginLeftButton;
    }
    marginLeft = 0;
    marginTop = marginTop + buttonHeight + marginLeftButton;
    }
    }

    // The listener thats run when the button is released
    void MainWindow::KeyClickListener()
    {
    myQPushButton *button = (myQPushButton *)sender();
    int opc = button->get_Tipo();
    switch (opc)
    {
    case 0:
    if ( this->timer->isActive() )
    {
    this->timer->stop();
    this->timer->deleteLater();
    delete this->timer;
    }

    break;
        case 1:
            changeGroupPage(button->get_Pagina(), GVars.getGroupMapButton());
            fillItemWidget(button->get_Pagina(), ui->ItemWidget);
            break;
        case 2:
            GVars.setSelectedGroup(button->get_id());
            fillItemWidget(button->get_Pagina(), ui->ItemWidget);
            break;
        case 3:
            fillGroupWidget(button->get_Pagina(), ui->GroupWidget, GVars.getGroupMapButton());
            break;
        case 4:
            //exit(0);
            break;
        default:
            break;
    }
    

    }

    // When the button is clicked
    void MainWindow::KeyTimerListener()
    {
    myQPushButton *button = (myQPushButton *)sender();
    int opc = button->get_Tipo();
    switch (opc)
    {
    case 0:
    this->timer = new QTimer(this);
    this->timer->setSingleShot(true);
    this->ItemToChange = button->get_id();
    connect(this->timer, SIGNAL(timeout()), this, SLOT(ItemConfigListener()));
    this->timer->start(2000);
    break;
    default:
    break;
    }
    }

    // this is called when the timeout is called
    void MainWindow::ItemConfigListener()
    {
    this->timer->stop();
    this->timer->deleteLater();
    delete this->timer;
    Form = new Config();
    Form->setItem(idbConnect.search_Item(this->ItemToChange));
    }
    @



  • okay, now that i have the code the next question is: what exactly are you doing with the mouse and what exactly happens.
    My understanding is that:

    1. you click the mouse over the button and hold the mouse pressed while you are over the button; after 2 seconds the Form opens
    2. you keep the mouse pressed while you are still over the button, then while keeping the mouse pressed you move away from the button and you still keep the mouse pressed; now, before you release the mouse button, another Form opens?

    The point is, i don't understand what exactly is your test, one move at a time.

    Also, what's that get_Typo()? are you sure your code goes through case 0?
    Also, why do you use deleteLater()?
    Also, why do you create and destroy a new timer every time, you just need one timer declared directly in your mainwindow class, and you start/stop it when needed

    Anywayz, why don't you just set up a breakpoint at ItemConfigListener() and play around and see exaclty when it gets triggered? (you can set your breakpoint to stop at the N-th pass, etc)



  • I did use the qDbug with messages to see what was hapening, when I move the mouse ( when simulating the long click ) I get the message of the qDebug that is on the KeyTimerListener(), that is called when I press the button, its like I was clicking the button again and I'm not.

    And no another form doesn't open when I move the mouse.

    I was using a new QTimer(this); because it was giving me an error, and puting it like that it as stoped.

    And yes I'm sure that he goes on to the 0 on the first switch case because of the qDebug() since I got the message from there also



  • i still don't understand what what exactly you are doing and what you mean by "simulating" the long click.

    Anywayz, i just tried a simple thing: placed a button on a form, connected it's "pressed" event to a on_myButton_pressed() method, and then:

    1. pressed the button and held the mouse pressed: the method gets executed: OK
    2. keep the mouse pressed and move the mouse out of the button area: the button is released (when the mouse moves away from being over it) and nothing happens: OK
    3. while still keeping the mouse pressed i come back over the button: the button gets pressed and the method is triggered again: OK
    4. while still keeping the button pressed i again move away from the button area: nothing happens: OK
    5. i release the mouse: nothing happens: OK

    Repeated the above for on_myButton_released(), and everything behaves again as expected: OK

    In conclusion, everything looks just fine with the qt events, apparently there's something wrong in your code that has nothing to do with the qt events



  • I need the long click because the software is for a touch that doesn't have the simulation of the right button, so this long click we could call it the right button.

    When I do the long click, it opens the form without problems, the problems starts when I move the mouse ( this is just to prenvent from from the click to hapen again when I just clicked once )

    For example here is the messages from the qDebug() with just one click ( the long click )

    @
    The button was pressed starting timer
    Form was open, stoping the timer
    The button was pressed starting timer
    case 0 of the KeyClickListener() that is triger because of the connect(Buttons,SIGNAL(pressed()),this,SLOT(KeyTimerListener()));
    The button was pressed starting timer
    case 0 of the KeyClickListener() that is triger because of the connect(Buttons,SIGNAL(pressed()),this,SLOT(KeyTimerListener()));
    @

    As you can see, I just clicked once but it sending the qDebug() that I have on the event KeyTimerListener() to be triguer when a click occours on the button.

    So I just did 1 click, but if I move the mouse its like clicking again as you can see on the code above of the qDebug()



  • I presume the OP has found a solution by now. I've just rolled my own which I'll add for completion.

    The QPushButton I'm working with is defined in a .ui file and the goal was to add a context menu to it.
    In the implementation file I declared a small new class (DiffContextMenu, inheriting QObject) that creates a QActionGroup containing the actions for my context menu and holding a pointer to the class instance (m_plugin) that the actions should connect to.

    getting a standard context menu is then very simple:

        m_editPatch.updateButton->setContextMenuPolicy(Qt::ActionsContextMenu);
        m_diffContextMenu = new DiffContextMenu(m_plugin);
        m_editPatch.updateButton->addActions(m_diffContextMenu->m_contextActions->actions());
    

    Since DiffContextMenu inherits QObject it can have an eventFilter() method which can then take the necessary actions to convert a click/tap-and-hold event into a contextmenu event;

        bool DiffContexMenu::eventFilter(QObject *obj, QEvent *event)
        {
            if (event->type() == QEvent::Gesture) {
                QGestureEvent *gEvent = static_cast<QGestureEvent*>(event);
                if (QTapAndHoldGesture *heldTap = static_cast<QTapAndHoldGesture*>(gEvent->gesture(Qt::TapAndHoldGesture))) {
                    if (heldTap->state() == Qt::GestureFinished) {
                        QPushButton *btn = dynamic_cast<QPushButton*>(obj);
                        if (btn) {
                            // user clicked and held a button, send it a simulated ContextMenuEvent:
                            QContextMenuEvent ce(QContextMenuEvent::Mouse, heldTap->position().toPoint(),
                                heldTap->hotSpot().toPoint());
                            gEvent->accept();
                            int ret = QCoreApplication::sendEvent(btn, &ce);
                            btn->setDown(false);
                            return ret;
                        }
                    }
                }
            }
            return false;
        }
    

    with that method in place all one still has to do is install the event filter on the target QPushButton, and tell it to react to the desired type of gesture:

        // make it possible to open the context menu with a click-and-hold:
        m_editPatch.updateButton->installEventFilter(m_diffContextMenu);
        m_editPatch.updateButton->grabGesture(Qt::TapAndHoldGesture);
    

    The only thing that might be a glitch in my implementation is the fact I have to "unclick" the button explicitly. I have a hunch that shouldn't be required but it's no big deal in itself.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.