1

it's my first question here on stackoverflow, but not the first time you help me out (I hope you can).

First I have to mention that I'm not that experienced with C++, especially with QT, where my problem is about. I have a problem with posting (or sending) QMouseEvents from code to a QTableWidget. I want to send multiple clicks to an MultiSelect QTableWidget and I expect, the clicked QTableWidgetItems are selected afterwards.

To reproduce the problem, I created a small sample. There is a button that sends the "clicks" to the QTableWidget (in my real app this is triggered by network). When the button is clicked, two "clicks" are executed (I also see that in the debugger), but only the first clicked TableWidgetItem is selected afterwards. I can't figure out why the second item doesn't get selected. I hope someone of you can :-). I really appreciate your help!

Here the sample code:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTableWidgetExamples w;
    w.show();
    return a.exec();
}

QTableWidgetExample.h:

#ifndef QTABLEWIDGETEXAMPLES_H
#define QTABLEWIDGETEXAMPLES_H

#include <QtGui/QMainWindow>
#include "ui_qtablewidgetexamples.h"

QT_BEGIN_NAMESPACE  // QT_BEGIN_NAMESPACE / QT_END_NAMESPACE are not needed in Qt user code
class QTableWidget; //forward declaration
class QTableWidgetItem; //forward declaration
QT_END_NAMESPACE

class QTableWidgetExamples : public QMainWindow
{
    Q_OBJECT

public:
    QTableWidgetExamples(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~QTableWidgetExamples();

private:
    Ui::QTableWidgetExamplesClass _ui;
    QTableWidget* _tableWidget;
    void clickItemFromCode(int index);

public slots:
    void btnClicked();
    void onItemClick(QTableWidgetItem* item);
};

#endif // QTABLEWIDGETEXAMPLES_H

QTableWidgetExample.cpp:

#include <QTableWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMouseEvent>
//#include <QTest>
#include "qtablewidgetexamples.h"

QTableWidgetExamples::QTableWidgetExamples(QWidget *parent, Qt::WFlags flags)
    : QMainWindow(parent, flags)
{
    _ui.setupUi(this);
    QVBoxLayout *vlay = new QVBoxLayout(this);

    _tableWidget = new QTableWidget(3,1,this);
    _tableWidget->verticalHeader()->hide();
    _tableWidget->horizontalHeader()->hide();
    _tableWidget->setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection);
    _tableWidget->setObjectName("list_1");
    _tableWidget->setItem(0, 0, new QTableWidgetItem("Item_0"));
    _tableWidget->setItem(1, 0, new QTableWidgetItem("Item_1"));
    _tableWidget->setItem(2, 0, new QTableWidgetItem("Item_2"));
    connect(_tableWidget, SIGNAL(itemClicked(QTableWidgetItem*)), this, SLOT(onItemClick(QTableWidgetItem*)));

    QPushButton *btn1 = new QPushButton("btn1");
    connect(btn1, SIGNAL(clicked()), this, SLOT(btnClicked()));

    vlay->addWidget(_tableWidget);
    vlay->addWidget(btn1);

    QWidget * wdg = new QWidget(this);
    wdg->setLayout(vlay);
    setCentralWidget(wdg);
}

QTableWidgetExamples::~QTableWidgetExamples() {}

void QTableWidgetExamples::onItemClick(QTableWidgetItem *item)
{
    bool isSel = item->isSelected();
    QString text = item->text();
}

void QTableWidgetExamples::btnClicked()
{
    clickItemFromCode(1);
    clickItemFromCode(2);   
}

void QTableWidgetExamples::clickItemFromCode(int rowIndex)
{
    float subX, subY;
    subX = subY = 0.5;

    QModelIndex index = _tableWidget->model()->index(rowIndex, 0);
    QRect rc = _tableWidget->visualRect(index); // view()->visualRect(index);
    QPoint pt(rc.x()+rc.width()*subX,rc.y()+rc.height()*subY);

    // just for debug purposes
    QTableWidgetItem* item = _tableWidget->item(rowIndex,0);
    QString itemText = item->text();

    QWidget *widget = (QWidget*) _tableWidget; //view();
    widget = widget->childAt(10,10);

    QEvent *e = QMouseEvent::createExtendedMouseEvent(QEvent::MouseMove,
            pt,widget->mapToGlobal(pt), Qt::LeftButton,Qt::LeftButton,0);
    QApplication::sendEvent(widget, e);

    e = QMouseEvent::createExtendedMouseEvent(QEvent::MouseButtonPress,
            pt,widget->mapToGlobal(pt), Qt::LeftButton,Qt::LeftButton,0);
    QApplication::sendEvent(widget, e);

    e = QMouseEvent::createExtendedMouseEvent(QEvent::MouseButtonRelease,
            pt,widget->mapToGlobal(pt), Qt::LeftButton,Qt::LeftButton,0);
    QApplication::sendEvent(widget, e);

    //QApplication::sendPostedEvents();
}

Edit to mbroadst answer:

Unfortunately it's a hard requirement. I inject my code into another application, where I read several of those QTableWidgets and send them via TCP to my application. That is working well, so far. Now I need to change those selections from my app and that's the problem. My preferred option is to do it with Mouse clicks. The intention behind that is that it then behaves as if a user is working in that application (but it seems that it isn't the case). But if you you have another idea, I'll give it a try. Maybe there's something better, that didn't came to my mind yet.

Jack
  • 11
  • 3
  • 1
    Is there a hard requirement that you simulate the mouse clicks? Or are you just trying to set those items as selected? – mbroadst May 15 '14 at 17:58

2 Answers2

0

You can use QTableWidgetItem::setSelected() in order to programmatically select an item. So your code would look like:

void QTableWidgetExamples::clickItemFromCode(int rowIndex)
{
    QTableWidgetItem *item = tableWidget->item(rowIndex, 0);
    item->setSelected(true);
}

Or, if you really need to simulate a mouse click then:

// NOTE: I haven't explicitly tested this, but its the general idea
void QTableWidgetExamples::clickItemFromCode(int rowIndex)
{
    QTableWidgetItem *item = tableWidget->item(rowIndex, 0);
    QRect visualItemRect = tableWidget->visualRectItem(item);

    QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonRelease, visualItemRect.center(),     
                                         Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QCoreApplication::sendEvent(tableWidget, event);
}
mbroadst
  • 768
  • 6
  • 8
  • Thanx again for your reply. The first solution that sets the selectedItems programmatically works. The second unfortunately not. Because it didn't compile, I edit the line that gets the QRect to: `QRect visualRectItem = tableWidget->visualRectItem(item);` – Jack May 16 '14 at 06:27
  • I updated the second example, is that all you needed to do to get it working? I'm being pretty lazy here :) – mbroadst May 16 '14 at 12:48
  • No, it just compiles then, but I was able to get it working. In my example the `QEvent::MouseMove` wasn't set up properly. I will post my working code later. There is still one problem, that the Mouse isn't moved to that postion. I mean, when I click the button, I expect the mouse moving to the item that is clicked, but that isn't the case yet – Jack May 16 '14 at 16:28
0

Finally I got a working solution:

The QEvent::MouseMove wasn't set up properly.

here is the corrected one. After exchanging this, the sample code does what it is supposed to do.

QEvent *e = QMouseEvent::createExtendedMouseEvent(QEvent::MouseMove,
            pt,widget->mapToGlobal(pt), Qt::NoButton, Qt::RightButton, Qt::NoModifier);
QApplication::postEvent(widget, e);

If you don't need to simulate mouse clicks, the first approach of @mbroadst (set selected items programmatically) would probably the better choice (thx for that).

Jack
  • 11
  • 3