mouse press over event and qtabwidget - qt

need that when a mouse ( with press button ) is on top of tab, it gets activated.
How is it possible.

You can intercept the mouseMoveEvent event from the QTabBar.
Edit: How To
To intercept it Qt offers you a great functionalities. You can install an event filter on a widget you have created so it will be able to get filtered event of its child widgets. See documentation about the installEventFilter function.

Just create your own EventFilter.
// TabBarEventHandler
class TabBarEventHandler : public QObject
{
public:
explicit TabBarEventHandler( QTabWidget * tabWidget );
virtual ~TabBarEventHandler();
void Initialize();
protected:
QTabWidget * mTabWidget;
virtual bool eventFilter( QObject* obj, QEvent* event ) override;
};
// TabBarEventHandler
TabBarEventHandler::TabBarEventHandler( TabBar * tabWidget )
: mTabWidget( tabWidget )
{
}
TabBarEventHandler::~TabBarEventHandler()
{
mTabWidget->tabBar()->removeEventFilter( this );
}
void TabBarEventHandler::Initialize()
{
mTabWidget->tabBar()->installEventFilter( this );
}
bool TabBarEventHandler::eventFilter(QObject* obj, QEvent* event)
{
qDebug() << event->type();
return QObject::eventFilter( obj, event );
}
void TabBarEventHandler::timerEvent(QTimerEvent* event)
{
}
And in your widget:
MyTabWidget::MyTabWidget( QWidget * parent ) : QTabWidget( parent )
{
mEventHandler = new TabBarEventHandler( this );
mEventHandler->Initialize();
}
MyTabWidget::~MyTabWidget()
{
delete mEventHandler;
}
The debug output:
QEvent::Type(HoverMove) QEvent::Type(HoverMove)
QEvent::Type(MouseButtonPress) QEvent::Type(InputMethodQuery)
QEvent::Type(InputMethodQuery) QEvent::Type(InputMethodQuery)
QEvent::Type(Paint) QEvent::Type(Paint)
QEvent::Type(MouseButtonRelease) QEvent::Type(HoverMove)
QEvent::Type(HoverMove) QEvent::Type(Leave)

Related

Memory leak from Promoted class of QDoubleSpinBox

I am promoting the QDoubleSpinBox class as i want to catch the mouseDoubleClick Event.
This is the Promoted class.
class SumDoubleBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SumDoubleBox(QWidget* parent = nullptr);
void setSingleStep(double val);
double singleStep() const;
void stepBy(int steps) override;
protected:
virtual void focusInEvent(QFocusEvent *e) override;
public slots:
void setZero();
void setOne();
signals:
int signalUndoRedo();
private:
double m_defaultStep = 1.0;
double m_CurrentStep;
bool m_stepUp;
};
SumDoubleBox::SumDoubleBox(QWidget* parent) : QDoubleSpinBox(parent)
{
SumLineEdit* lineEdit = new SumLineEdit(this);
setLineEdit(lineEdit);
setMinimum(0.0);
setMaximum(99999.0);
}
Since i am creating a pointer in the Constructor of the SumDoubleBox Class.
SumLineEdit* lineEdit = new SumLineEdit(this);
Do i need to Explicitly delete this in the Destructor ?
/////////////////////////////////////////////////////////////////
The Defination of the SumLineEdit class.
class SumLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit SumLineEdit(QWidget* parent = nullptr) { };
protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
};
void SumLineEdit::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
selectAll();
event->accept();
return;
}
QLineEdit::mouseDoubleClickEvent(event);
}
Since you've parented the new lineedit to the SumDoubleBox, it should get deleted with its parent.
There is, however, a more Qt-centric way to handle this case that I would strongly recommend you use or at least look into: Installing an event filter on the built-in line edit and handling the events for it. I've made many spinbox variants, and this approach usually works out best:
SumDoubleBox::SumDoubleBox( QWidget *parent ) :
QDoubleSpinBox( parent )
{
lineEdit()->installEventFilter( this );
}
bool SumDoubleBox::eventFilter( QObject *object, QEvent *event )
{
if( object == lineEdit() && event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
if( mouseEvent->button() == Qt::LeftButton )
{
selectAll();
event->accept();
return true; //swallow the event
}
}
return false; //let the event through to the lineedit
}
This makes the custom class entirely redundant. It's nice because inheritance is a big hammer that easily gets out of control as a codebase scales up, and if there are other simpler ways to achieve your goal, it's often worth considering.

Qt: How to display a text consistently on the cursor, not depending on cursor position?

I want a text to be displayed persistently on the curser event when the cursor is moving, not depending on the cursor position. I used Qtooltip for this purpose. This is the code to show the text:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// ...
}
bool Widget::event (QEvent *ev)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(ev);
QToolTip::showText(helpEvent->globalPos(), "Something got it");
return false;
}
return QWidget::event(ev);
}
But when I run this code the text is not displayed consistently and it shows up only sometimes, disappears while moving the cursor, and the whole window flickers.
You can probably achieve what you want by intercepting mouse move events rather than the tool tip notifications...
class tooltip_event_filter: public QLabel {
using super = QLabel;
public:
tooltip_event_filter ()
{
setWindowFlags(windowFlags()
| Qt::BypassWindowManagerHint
| Qt::FramelessWindowHint
);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::MouseMove) {
/*
* Note the QPoint(1, 0) offset here. If we don't do that then the
* subsequent call to qApp->widgetAt(QCursor::pos()) will return a
* pointer to this widget itself.
*/
move(QCursor::pos() + QPoint(1, 0));
if (const auto *w = qApp->widgetAt(QCursor::pos())) {
setText(QString("widget#%1").arg((qulonglong)w));
show();
} else {
hide();
}
}
return super::eventFilter(obj, event);
}
};
Then install an instance of tooltip_event_filter on the application instance...
tooltip_event_filter tooltip_event_filter;
qApp->installEventFilter(&tooltip_event_filter);
The example shown simply displays the address of the widget under the mouse pointer as it's moved.

Event handling : Accessing another widget's property

OK, this should be easy.
I tried to handle drop event onto a QGraphicsView widget. Incoming data dragged from a QTreeView widget. For that, I re-implemented these methods:
void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
this.scene()->addPixmap(pixmap);
}
This works fine; but how can I change another graphicsview scene within this widget's drop event? That is:
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
// I cannot access ui; and cannot access my widgets...:
ui->anotherview->scene()->addPixmap(pixmap);
}
What about making a custom signal in your QGraphicsView like void showPixmap(QPixmap p) and connecting it to a slot in your main gui class where you can access ui elements. You can then call emit showPixamp(pixmap) in the dropEvent.
Subclassing QGraphicsView
//header file
class CustomView : public QGraphicsView
{
public:
CustomView(QGraphicsScene*, QWidget*=NULL);
~CustomView();
signals:
void showPixmap(QPixmap p);
protected:
virtual void dropEvent(QDropEvent *event);
};
//cpp file
CustomView::CustomView(QGraphicsScene *scene, QWidget* parent)
:QGraphicsView(scene, parent)
{
//if you need to initialize variables, etc.
}
void CustomView::dropEvent(QDropEvent *event)
{
//handle the drop event
QPixmap mPixmap;
emit showPixmap(mPixmap);
}
Using event filters in your main GUI class
void GUI::GUI()
{
ui->mGraphicsView->installEventFilter(this);
}
bool GUI::eventFilter(QObject *object, QEvent *event)
{
if (object == ui->mGraphicsView && event->type() == QEvent::DropEnter) {
QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
//handle the drop event
return true;
}
else
return false;
}

MousePressEvent in view and items in Qt

I have a custom QGraphicsView and a custom QGraphicsItem. I want the Item to handle my click if I click in the item, else I want the click to be handled by the View.
But when I click on the item, the item handles the click. This is ok. But if I click somewhere else the click isn't handled at all. All the code in my classes that have anything to do with mouseEvents is below.
class CustomView : public QGraphicsView
{
Q_OBJECT
public:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
void CustomView::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in view";
}
class CustomItem : public QGraphicsItem
{
public:
CustomItem(CustomView* widget)
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
CustomItem::CustomItem(CustomView* widget){
setFlag(ItemIsSelectable);
setFlag(ItemIsMovable);
}
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in item";
}
It seems that when I remove the mousePressEvent function from the CustomItem class and change in the CustomView the mousePressEvent function to:
void CustomView::mousePressEvent(QMouseEvent *event){
cout << "pressing in view";
}
the CustomView handles all the mouseEvents.
How can I let the CustomItem handles the clicks in the items and the CustomView handle all the other clicks?
Thank you.
EDIT
So now I have changed it to:
class CustomView : public QGraphicsView
{
Q_OBJECT
public:
};
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in scene";
}
class CustomItem : public QGraphicsItem
{
public:
CustomItem(CustomView* widget)
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
CustomItem::CustomItem(CustomView* widget){
setFlag(ItemIsSelectable);
setFlag(ItemIsMovable);
}
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in item";
}
A Click in the scene but not in an item get handled by the scene. But clicks in the items
dont get handled by the items itself, instead it get handled by the scene. Unless if you click 2 times really fast on the items it gets handled by the scene and the item.
Any ideas?
QGraphicsView isn't really a good place to handle scene-specific events. Instead, you'll need to override QGraphicsScene::mousePressEvent. I would recommend something like this:
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (itemAt(event) == NULL)
{
// do stuff if not clicked on an item
}
else
{
QGraphicsScene::mousePressEvent(event); // this forwards the event to the item
}
}
Note that QGraphicsScene::itemAt might give you difficulties if you have items with the ItemIgnoresTransformations flag set to true. Look to the docs for QGraphicsScene::itemAt for how to resolve this.

How to identify when the current tab is changing in a QTabWidget?

I'm using a QTabWidget and I need a way to handle the change of the current tab before it actually happens, and possibly cancel it if certain conditions are met. The QTabWidget::currentChanged signal is received after the current tab has changed, but is there a QTabWidget::currentChanging signal or another way to achieve the behavior I need?
In my case, I connect SIGNAL and SLOT like this:
//check if user clicked at a tab
connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSelected()));
and in tabSelected() function, I check current Tab Index:
void MainWindow::tabSelected(){
if(ui->tabWidget->currentIndex()==0){
// Do something here when user clicked at tab1
}
if(ui->tabWidget->currentIndex()==3){
// Do something here when user clicked at tab4
}
}
This is how I solved it
void MainWindow::on_tabWidget_currentChanged(int index)
{
if (lockTabs) ui->tabWidget->setCurrentIndex(lockedTab);
}
On click of a button, I set lockTabs to true and save the current tab index to lockedTab (int). No matter what tab you click it will just throw you back to the locked tab.
I do agree with the first comment that disabling tabs is the better way tho. This is my solution for disabling tabs:
void MainWindow::lockTabs(int except){
for (int i=0; i<ui->tabWidget->count(); i++) {
if (i!=except) ui->tabWidget->setTabEnabled(i, false);
}
}
void MainWindow::unlockTabs() {
for (int i=0; i<ui->tabWidget->count(); i++) {
ui->tabWidget->setTabEnabled(i, true);
}
}
In your header, declare:
QWidget *saveTab
Create a routine tabChanged have the slot for the currentChanged() signal. Then:
void pkgName::tabChanged
//"ask your question"
if "bad reply"
// This is where you'll "set back to your old tab"
ui->tabWidget->setCurrentWidget(savedWidget)
end if
savedWidget = ui->tabWidget-> getCurrentWidget()
// Process
Using a regular QTabWidget and flipping back to previous tab after currentChanged was emitted if change was forbidden does not look right for user as the new tab is made visible before the previous one is re-selected, this is due to QTabWidget informing the tab "changed", not "is about to change".
One option is to create your own QTabWidget. Thanks to QTabBar, this is pretty obvious.
You'll also need to create QTabWidget like function to use it "like" a QTabWidget, but there's not so many function you'll need.
Here is an example of QTabWidget like class with a aboutToChangeTab signal being emitted informing that tab is about to be changed, one may set allowed to false to forbid tab change.
#pragma once
#include <QWidget>
class QTabBar;
class QStackedWidget;
class SmartTabWidget : public QWidget
{
Q_OBJECT
typedef QWidget baseClass;
public:
SmartTabWidget( QWidget* parent );
int addTab(QWidget* page, const QString& label);
int addTab(QWidget* page, const QIcon& icon, const QString& label);
int currentIndex() const;
QWidget* widget( int index );
signals:
void aboutToChangeTab( QWidget* curTab, QWidget* nextTab, bool* allowed );
private slots:
void tryToChangeTab( int index );
private:
QTabBar* m_tab;
QStackedWidget* m_stack;
};
And:
#include "smart_tab_widget.h"
#include <QTabBar>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QIcon>
SmartTabWidget::SmartTabWidget( QWidget* widget ) :
baseClass( widget )
{
new QVBoxLayout( this );
layout()->setContentsMargins( 0,0,0,0 );
layout()->addWidget( m_tab = new QTabBar(this) );
layout()->addWidget( m_stack = new QStackedWidget(this) );
connect(m_tab, SIGNAL(currentChanged(int)), this, SLOT(tryToChangeTab(int)));
}
int SmartTabWidget::addTab(QWidget* page, const QString& label)
{
return addTab( page, QIcon(), label );
}
int SmartTabWidget::addTab(QWidget* page, const QIcon& icon, const QString & label)
{
m_stack->addWidget( page );
int index = m_tab->addTab( icon, label );
assert( m_stack->count() == index+1 );
return index;
}
int SmartTabWidget::currentIndex() const
{
return m_tab->currentIndex();
}
QWidget* SmartTabWidget::widget( int index )
{
return m_stack->widget( index );
}
void SmartTabWidget::tryToChangeTab( int index )
{
int currentIndex = m_stack->currentIndex();
bool canChange = true;
emit aboutToChangeTab( m_stack->widget( currentIndex ),
m_stack->widget( index ),
&canChange );
if ( canChange )
{
m_stack->setCurrentIndex( index );
}
else
{
// prevent this function to be called again
bool blocked = m_tab->blockSignals( true );
// unselect requested tab as change is not allowed
m_tab->setCurrentIndex( currentIndex );
m_tab->blockSignals( blocked );
}
}
One can connect aboutToChangeTab to a slot (allowTabChange) and do something like:
void MyWidget::allowTabChange( QWidget* curTab, QWidget* nextTab, bool* allowed )
{
if ( !canChange( curTab, nextTab ) )
*allowed = false;
}
There is a simple solution with event filters that doesn't require to subclass QTabWidget. In my case I needed to disable switching to a particular tab
ui->tabWidget->tabBar()->installEventFilter(this);
then:
bool MainWindow::eventFilter(QObject* watched, QEvent* event)
{
if(watched == ui->tabWidget->tabBar())
{
if(event->type() == QEvent::MouseButtonPress)// || event->type() == QEvent::MouseButtonRelease)
{
auto pos = dynamic_cast<QMouseEvent*>(event)->pos();
auto index = ui->tabWidget->tabBar()->tabAt(pos);
if(ui->tabWidget->widget(index) == ui->addButtonTab)
return true; // cancell event
}
}
return QMainWindow::eventFilter(watched, event);
}
At the stage of mouse click it is possible to retrieve index of a currently selected tab and prepare it for being switched(or cancel switching as done in my example).

Resources