Qt::QTableWidget: SIGNAL currentCellChanged: Differ between mouse and keyboard - c++

I have a QTableWidget that emits the SIGNAL currentCellChanged calling a SLOT method whenever I select a row with the mouse or keyboard (tab or arrow keys). Is it possible to find out which one was used (mouse or keyboard)?

I assume you're using a regular Designer form class and the table widget is part of that form.
In constructor of your form class you should do:
ui->tableWidget->viewport()->installEventFilter(this);
Add eventFilter method to your class (it reimplements virtual QObject::eventFilter):
bool MyForm::eventFilter(QObject* object, QEvent* event) {
if (object == ui->tableWidget->viewport()) {
if (event->type() == QEvent::KeyPress) {
method = method_keyboard;
} else if (event->type() == QEvent::MouseButtonPress) {
method = method_mouse;
}
}
return false;
}
In the slot you can check the value of method variable to find out which control was used.

Related

How to call a user method after show method in qt c++ [duplicate]

In Delphi I often made an OnAfterShow event for the main form. The standard OnShow() for the form would have little but a postmessage() which would cause the OnafterShow method to be executed.
I did this so that sometimes lengthy data loading or initializations would not stop the normal loading and showing of the main form.
I'd like to do something similar in a Qt application that will run on a desktop computer either Linux or Windows.
What ways are available to me to do this?
You can override showEvent() of the window and call the function you want to be called with a single shot timer :
void MyWidget::showEvent(QShowEvent *)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
This way when the windows is about to be shown, showEvent is triggered and the doWork slot would be called within a small time after it is shown.
You can also override the eventFilter in your widget and check for QEvent::Show event :
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
return false;
}
When using event filter approach, you should also install the event filter in the constructor by:
this->installEventFilter(this);
I solved it without a timer using Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}

Qt. Ignore any mouse events in a widget over another one

I have a QTableView where a small mark appears if the mouse hover on active cell. This mark is a widget and emit a signal if hover, changing the selecion mode of the table when I drag over the table.
The problem is that if I am over the mark, I can't drag over the table.
Things I have tried:
Set the widget mark like setWindowFlags(Qt::WindowTransparentForInput);, but I can't use it because I need hover event.
Ignore the events in the widget mark using event->ignore() or sending the event to parent using and eventFilter:
bool EventFilterMarca::eventFilter(QObject *obj, QEvent *event)
{
if( event->type() == QEvent::HoverMove)
{
....
}
else if (event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove ||
event->type() == QEvent::MouseButtonDblClick)
{
//QApplication::sendEvent(parent(),event);//one try
//event->ignore();//another try
return QObject::eventFilter(obj,event);;
}
}
Subclassing mousePressEvent, mouseReleaseEvent and mouseMoveEvent in the mark widget and call to parent class. Looks like if it works (pass the event to parent) into the current cell of the table (the parent):
void Marca::mousePressEvent(QMouseEvent *event)
{
//event->setAccepted(false);
if(event->buttons() == Qt::LeftButton)
{
MiTabla* tabla = qobject_cast<MiTabla*>(parent());
if (tabla)
{
tabla->mousePressEvent(event);
//QApplication::sendEvent(parent(),event);
}
}
//event->ignore();
}
Well, the question is how could get that the behaviour of the table was the same if I am over the widget of the cell or directly on the cell.
Also I add a link with my first approach to getting it. It works but the code is awful and not easy to follow:
https://github.com/exodehm/tablacalc
I think the event filter is not what you want. This is what makes a widget not receive specific events. What you actually want to do is to mark the event to be unhandled by specific widget, putting it higher in the class for handling. In order to do that you should try and reimplement either the widgets ::event method, or more specific handler.
In qt docs it says that:
bool QWidget::event(QEvent *event)
This function returns true if the event was recognized, otherwise it returns false. If the recognized event was accepted (see QEvent::accepted), any further processing such as event propagation to the parent widget stops.
So what I think you should be doing is basically marking event as not accepted when specific event type you want to propagate to parent widget happens for your widget.
So in your case I would expect something like:
void QWidget::dropEvent(QDropEvent *event) {
event->setAccepted(false);
}
Also in order for this to work your table needs to be parent widget of the mark widget.

Disable KeyEvent for "unneeded QWidget?

I have a QDockWidget in my Mainwindow with a QTableWidget and two QPushbuttons.
Of course, I can click the buttons with my mouse, but I want also to "click" them with left- and right-arrow-key.
It nearly works perfect. But before they are clicked via key, it seems like the focus jumps to the right/left of the QTableWidget (the items in it, it goes through all columns).
Is it possible that I have the KeyPressEvents only for the buttons in the QDockWidget?
You can use an event filter like this:
class Filter : public QObject
{
public:
bool eventFilter(QObject * o, QEvent * e)
{
if(e->type() == QEvent::KeyPress)
{
QKeyEvent * event = static_cast<QKeyEvent *>(e);
if((event->key() == Qt::Key_Left) || (event->key() == Qt::Key_Right))
{
//do what you want ...
return true;
}
}
return QObject::eventFilter(o, e);
}
};
keep an instance of the filter class in your main window class:
private:
Filter filter;
then install it in your widgets, e.g. in your main window class constructor:
//...
installEventFilter(&filter); //in the main window itself
ui->dockWidget->installEventFilter(&filter);
ui->tableWidget->installEventFilter(&filter);
ui->pushButton->installEventFilter(&filter);
//etc ...
You may want to check for modifiers (e.g. Ctrl key), to preserve the standard behaviour of the arrow keys:
//...
if(event->modifiers() == Qt::CTRL) //Ctrl key is also pressed
{
if((event->key() == Qt::Key_Left) || (event->key() == Qt::Key_Right))
{
//...

Right click on QTreeView item

I want to generate the right click menu from the entry of a QTreeView. Currently I tried this, but I don't want the whole treeView to generate the right click and then me to filter the position on which the mouse is. I want that the signal to be generated from the entry.
connect(mtreeView, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(showContextMenu(const QPoint&)));
Thanks!
Method 1
It is better to use the ContextMenuEvent rather than MouseReleaseEvent as it is a more portable way to trigger the context menu, will support accessibility on certain platforms, etc... The right click is not the only way to open a context menu.
If you do not want to subclass QTreeView , install an event handler from the main window:
ui->myTreeView->installEventFilter(this);
Then handle the event in the main window filterEvent
bool MainWindow::eventFilter(QObject *target, QEvent *event)
{
if (target == ui->myTreeView)
{
QContextMenuEvent* m = dynamic_cast<QContextMenuEvent*>(event);
if (event->type() == QEvent::ContextMenu && e!=0)
{
//Create context menu here
return true;
}
}
return false;
}
Method 2
Change the context menu mode to a signal:
ui->myTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->myTreeView, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(treeCustomMenu(QPoint)));
Then implement your slot:
void MainWindow::treeCustomMenu(const QPoint & pos)
{
//Implement your menu here using myTreeView->itemAt(pos);
}
What I do is to override mouseReleaseEvent and check manually.
void MyTreeView::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton) {
QTreeWidgetItem *item = itemAt(e->pos());
if (item) {
QMenu m;
m.addAction("hello");
m.addAction("world");
QAction *selected = m.exec(mapToGlobal(e->pos()));
if (selected) {
qDebug() << "selected" << selected->text();
}
}
} else {
QTreeView::mouseReleaseEvent(e);
}
}
What you mean by the entry is not represented by a QObject in Qt. Only the item model is a QObject, but the individual tree nodes are not QObjects in Qt item/view system.
Therefore, they cannot emit any signal

Take action after the main form is shown in a Qt desktop application

In Delphi I often made an OnAfterShow event for the main form. The standard OnShow() for the form would have little but a postmessage() which would cause the OnafterShow method to be executed.
I did this so that sometimes lengthy data loading or initializations would not stop the normal loading and showing of the main form.
I'd like to do something similar in a Qt application that will run on a desktop computer either Linux or Windows.
What ways are available to me to do this?
You can override showEvent() of the window and call the function you want to be called with a single shot timer :
void MyWidget::showEvent(QShowEvent *)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
This way when the windows is about to be shown, showEvent is triggered and the doWork slot would be called within a small time after it is shown.
You can also override the eventFilter in your widget and check for QEvent::Show event :
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
return false;
}
When using event filter approach, you should also install the event filter in the constructor by:
this->installEventFilter(this);
I solved it without a timer using Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}