Different default button design behaviour - c++

The problem is the following: I want to redefine (get rid of hovering effect of the button) the default Qt button style's behaviour. I only need 1 and 3 as shown on the picture under. The 2nd look comes when the button is focused.
Hovering effect: appears when you hover the button; remains painted when you press the button and move the cursor outside the space of the button.
What I've tried:
Redefining the events:
How do I implement QHoverEvent in Qt?
Here I simply redefined some of the events like Move, Hover etc. and made them non-functional.
Example Code:
class TestButton : public QToolButton
{
Q_OBJECT
public:
TestButton (QWidget *parent = 0) :
QToolButton(parent)
{}
bool event(QEvent * e)
{
this->clearFocus();
this->clearMask();
switch (e->type())
{
case QEvent::GraphicsSceneHoverEnter:
case QEvent::GraphicsSceneHoverLeave:
case QEvent::GraphicsSceneHoverMove:
case QEvent::HoverEnter:
case QEvent::HoverLeave:
case QEvent::HoverMove:
return true;
default:
return QWidget::event(e);
}
}
};
Some minor hacks:
btn->setFocusPolicy(Qt::NoFocus);

If you accept only HoverEnter events then after releasing the button the hover effect will go off as soon as you move mouse away. So you need to pretend moving mouse away by sending a corresponding event.
bool event(QEvent * e)
{
switch (e->type()) {
case QEvent::HoverEnter:
return true;
case QEvent::MouseButtonRelease: {
QEvent event(QEvent::Leave);
QApplication::sendEvent(this, &event);
}
default:
return QWidget::event(e);
}
}

Related

How to create Mouse right click menu option using eventFilter in Qt?

I have a QGraphicsView which contains many QGraphicsItem. If I click mouse right click on any QGraphicsItem, the item should get select and right menu options should appear and then I will choose one of the options among them.To do that I have installed eventFilter and through it, I am using ContextMenu to create right click menu. Right click menu are getting cretaed properly. But propblem is I am not getting how to connect them to some function so that I can write logic for it.
It means if I clicked on save option that particular QGraphicsItem should get select and I should be able to go to some function where I will write logic for saving.
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
switch(event->type())
{
case QEvent::ContextMenu:
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
menu = new QMenu(this);
option = menu->addMenu("CopyOption");
option->addAction("save");
menu->exec(mouseEvent->globalPos());
break;
}
default:
break;
}
}
In your approach you show a context menu when you have no information if any item is selected. It is rather bad idea. You don't want to show the context menu in any location of view. You have to check if a cursor mouse is over an item.
Why not to derive from QGraphicsItem and just overload mousePressEvent method. Inside this method check if right button of mouse is clicked. If so, show context menu and test which action is clicked. Minimal code would be:
class TItem : public QGraphicsItem
{
bool _selected = false;
public:
TItem(QGraphicsItem* parent = nullptr) : QGraphicsItem(parent) {}
QRectF boundingRect() const override { return QRectF(0,0,20,20); }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override {
painter->fillRect(QRectF(0,0,20,20),_selected ? QColor(0,255,255) : QColor(255,255,0));
}
void mousePressEvent(QGraphicsSceneMouseEvent* e) override {
QGraphicsItem::mousePressEvent(e);
if (e->button() & Qt::RightButton) {
QMenu menu;
QAction* a1 = menu.addAction(QString("test1"));
QAction* a2 = menu.addAction(QString("test2"));
if(a2 == menu.exec(e->screenPos())) {
test2();
_selected = true;
update();
}
}
}
void test2() {
QMessageBox::information(nullptr,"test2","test2");
}
};
All the job with checking is an item is under mouse is done by QT, mousePressEvent is invoked only if it is necessary.
Another approach would be override mousePressEvent on QGraphicsView. Inside which:
get all items belonging to the scene
iterate over them, checking if an item is under mouse -> QGraphicsItem has isUnderMouse method
if any item is under mouse, create QMenu and show it
check selected QAction, if it is save call a proper method which doing the save and mark the item as selected

How to display some text besides mouse pointer when the mouse hover over QGraphicsItem's in Qt?

I have a QGraphicsView, which contains many rectangle and polylines. I wanted to print every object names ( every rectangle, polylines have names) once I clicked them on view through mouse click. I did that and worked perfectly.
But now I want to do that over mouse hovering. It means, if I hover a mouse over particular object, it should show its name besides cursor.
I tried that, but
I am not understanding, on hovering, how that particular object should get selected ?
And how to print its name besides cursor point ?
I tried this way:
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
bool filterEvent = false;
switch(event->type())
{
case QEvent::MouseButtonPress:
{....}
case QEvent::MouseButtonRelease:
{...}
case QEvent::Enter:
{
QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event);
QPointF po = _view->mapToScene(mouseEvent->pos());
FindNamesOverHover(po);
}
return true;
default:
break;
}
return filterEvent;
}
void myClass::FindNamesOverHover(QPointF p)
{
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(_scene->itemAt(p,QTransform()));
if(rItem)
{
// some logic
qDebug()<< "Instance name is " << i->Name();
}
}
Constructor of class myClass
myClass::myClass(QWidget* parent) :
QDockWidget(parent)
{
scene = new QGraphicsScene(this);
view = new QGraphicsView(this);
view->setScene(_scene);
view->viewport()->installEventFilter(this);
}
Now above code works only,
When I select object through mouse click and then again hover over it.
Then it prints its name on console.
But I want only hovering ( no selection through mouse click) will show its name besides cursor point.
Can any one help me ?
You can override QGraphicsScene.mouseMoveEvent and inside it use QGraphicsScene.itemAt to query for item under cursor. To display you can use QGraphicsTextItem, just move it to correct position and apply text.

How to set focus for button in KeyPressEvent for key_up, key_down. QT

I have a Form with buttons called "Up", "Down", etc.
I need to process keyboard buttons (arrow_up, arrow_down, etc). Parallelly I want to set focus for relevant buttons.
For Example: If I pressed the arrow_down button on keyboard then the "Down" button would be a focus on my Form.
My variant how to do this:
bool ClientWindow::eventFilter(QObject *obj, QEvent *e)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *event= dynamic_cast<QKeyEvent*>(e);
switch(event->key()) {
case Qt::Key_Up: ui->up->setFocus(); ;break;
case Qt::Key_Down: ui->down->setFocus(); break;
case Qt::Key_Left: ui->left->setFocus(); break;
case Qt::Key_Right: ui->right->setFocus(); break;
}
}
return QObject::eventFilter(obj, event);
}
But, focus isn't set, and
1) If I return true from eventFilter then focus wouldn't set.
2) If I return QObject::eventFilter(obj, event) then focus would be transferred to next Object.
How to set focus for the relevant button?
Since you're trying to change a standard feature of Qt (arrow keys move focus just like TAB key does), you may want to try an extreme solution.
The point is: are your arrow key press events even being filtered at all? My guess is no. Something above (or before) the widget is consuming them and keeps the standard behavior unchanged (focus move from button to button according to their tab-indexes as usual).
I would try installing your filter onto the QApplication object, in the widget constructor this way:
qApp->installEventFilter(this);
The filter should look like this:
bool Widget::eventFilter(QObject *obj, QEvent *e)
{
if (e->type() == QEvent::KeyPress)
{
if(qApp->activeWindow() == this)
{
QKeyEvent *event= dynamic_cast<QKeyEvent*>(e);
switch(event->key()) {
case Qt::Key_Up: ui->up->setFocus(); break;
case Qt::Key_Down: ui->down->setFocus(); break;
case Qt::Key_Left: ui->left->setFocus(); break;
case Qt::Key_Right: ui->right->setFocus(); break;
default:
return qApp->eventFilter(obj, e);
}
return true;
}
}
return qApp->eventFilter(obj, e);
}
Please notice this line:
if(qApp->activeWindow() == this)
which should prevent the rule in the filter to be applied whenever the arrow keys get pressed (i.e. when the widget isn't the topmost one). You can ignore that, if your application hasn't other widget than this one.
Final advice: try to never change the standard UI behavior. Users do expect things to work as usual, and solutions that look absolutely cool to the developer, sometimes (often), brings no improvements at all and only confuse the user.

Qt: accept mouseRelease only when there was no drag and drop

I have a subclass of QGraphicsView that should accept two kinds of mouse events: drag and drop for scrolling and simple clicks for item selection/highlight.
So I use
setDragMode(QGraphicsView::ScrollHandDrag);
to enable scrolling the view with the "Hand". And I have a function like this:
void GraphView::mouseReleaseEvent(QMouseEvent* e)
{
if (e->button() == Qt::LeftButton)
emit leftClicked(mapToScene(e->pos()));
else
emit rightClicked(mapToScene(e->pos()));
QGraphicsView::mouseReleaseEvent(e);
}
which creates signal whenever the user clicks on the scene.
However, the problem is: when I stop dragging and release the mouse button, the mouseReleaseEvent function is called, and if the cursor happens to be over some element of the scene, it will get highlighted.
How can I changed the mouseReleaseEvent function so that the signals are created only if there was no previous drag of the mouse?
If you use mousePress and mouseMove in combination with mouseRelease, then you can determine what mouse action the user just performed.
If you have mousePress then mouseRelease, then it must be a simple click.
If you have mousePress, mouseMove, and then mouseRelease, then it must be a drag.
The Qt documentation contains an example of interpreting combinations of mouse events in action in a scribbling program.
http://doc.qt.io/qt-4.8/qt-widgets-scribble-example.html
You can extend the principle to something like this:
private bool MyWidget::dragging = false;
private bool MyWidget::pressed = false;
void MyWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
pressed=true;
}
QGraphicsView::mousePressEvent(event)
}
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && pressed)
{
dragging=true;
}
QGraphicsView::mouseMoveEvent(event)
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && pressed) {
pressed = false;
if (dragging)
{
dragging = false;
}
else
{
// plain mouse click
// do something here
}
}
QGraphicsView::mouseReleaseEvent(event)
}
Note that this does not address edge cases where a user's mouse action is performed only partially inside the widget. I must also admit that I am relatively new to Qt and have not yet used ScrollHandDrag, but this is how one would go about identifying a certain combination of mouse events.

How to know exactly when a user expands a QTreeView item?

Is there a way to know exactly when an expand operation begins when a user, lets say clicks the expand arrow in a QTreeView?
For the case when he double clicks I can catch the double-click event.
I tried reimplementing this slot void expand(const QModelIndex &index); from QTreeView but it doesn't seem to work.
There is a signal called void expanded(const QModelIndex &index); in QTreeView but it seems to be sent after the expansion happened.
I am using QT 4.8.2
This is what I did to get the functionality I needed:
I reimplemented the mousePressEvent from QTreeView like this
void MyTreeView::mousePressEvent(QMouseEvent *event)
{
QModelIndex clickedIndex = indexAt(event->pos());
if(clickedIndex.isValid())
{
QRect vrect = visualRect(clickedIndex);
int itemIdentation = vrect.x() - visualRect(rootIndex()).x();
if(event->pos().x() < itemIdentation)
{
if(!isExpanded(clickedIndex))
{
//do stuff
}
}
}
}
I check to see if the mouse press is left to the text label of the item(meaning on the expand arrow)
This combined with the double click event gives me what I needed.
You could try to this by implementing a method for a change event:
void YourTreeView::changeEvent(QEvent *e)
{
QFrame::changeEvent(e);
switch (e->type()) {
case QEvent::QGraphicsSceneResizeEvent
//do want you want to do
break;
default:
break;
}
}