Implementing mousePressEvent and setDragMode in Qt - c++

I am doing a project in which I should implement mousePressEvent and setDragMode on QGraphicsScene. I am able to implement individually but I should implement both at the same time. I included both into the code but only mousePressEvent is working and not able to drag the scene . Could you please anyone tell me that how to implement both.Please find the attached code. Thankyou.
MyGraphicsView.cpp
MyGraphicsView::MyGraphicsView(QWidget* parent) : QGraphicsView(parent) {
Scene = new QGraphicsScene(this);
setScene(Scene);
setDragMode(ScrollHandDrag);
}
void MyGraphicsView::mousePressEvent(QMouseEvent *eve)
{
if (eve->button() == Qt::LeftButton) {
// handle Left mouse button here
QPointF pt = mapToScene(eve->pos());
}
}
MyGraphicsView.h
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT
protected:
virtual void mousePressEvent(QMouseEvent *eve);
}

From QGraphicsView Qt docs on DragMode property:
(...) The default value, NoDrag, does nothing.
This behavior only affects mouse clicks that are not handled by any item. You can define a custom behavior by creating a subclass of QGraphicsView and reimplementing mouseMoveEvent().
This is probably why you can't make a DragMode to get to work when you implement a mousePressEvent: The event is processed, so the Qt framework thinks the "job is done", and no need to trigger a drag.
The doc suggest to re-implement the mouseMoveEvent. It still provides a QMouseEvent, so you can get the coordinates, and the information that what mouse button is currently pressed. From that on you only have to implement a state machine for Idle; StartDrag; Drag; FinishDrag
according to what buttons are pressed during a move, and implement your desired functionality in whichever states you need them.

Related

mouse tracking does not work In the widget in the main widget

I implemented showing button when mouse is under QTextEdit in the QWidget.
when I test code, I found it sometimes didn't work showing button
bug cause is that MouseMoveEvent doesn't work in Qwidget when I move in the QTextEdit.
I don't understand why MouseMoveEvent doesn't work...
here is my code
this->setMouseTracking(true); // this is QWidget
ui.textEdit_log->setMouseTracking(true); // this is textEdit
void QWidget::mouseMoveEvent(QMouseEvent* event) // reImplemented mouseMoveEvent
{
if (ui.textEdit_log->underMouse())
{
m_pButton->show();
}
else
{
m_pButton->hide();
}
}
Mouse tracking for QTextEdit will only work by subclassing QTextEdit and reimplementing it's mouseMoveEvent() from where you can pass the event up to the parent's mouseMoveEvent().
OR
Instead of using ui.textEdit_log->setMouseTracking(true) use ui.textEdit_log->setAttribute(Qt::WA_TransparentForMouseEvents) this will make your textedit_log invisible for mouse events. In this case you can use the childrenRect() function of your QWidget to determine if your mouse is over the textedit_log.
As far as software design goes, subclassing is a conceptually very heavy operation - it indicates a very deep coupling. It should be used with due concern for the maintainability impact it has. An almost no case should subclassing be used to merely observe the state of an object! - any API that forces you to do that is essentially broken.
Qt provides convenience "event listener" methods, but these methods are not meant to be used as external observers, i.e. you should never override them if all you want is to observe the objectfrom outside. The event filters should be used instead, since they introduce the weakest of possible couplings, and are very generic.
The mouse tracking events will be sent to the widget that tracks the mouse cursor - the editor widget. So no other widget will hear them. You have to implement QObject::filterEvent in a class of your choice (whatever class is convenient), then install that event filter on the editor widget. Your filter will intercept all events sent to the editor, including mouse move events. You should not subclass QTextEdit - use event filters instead!
In any case, the idea to do this via mouse movement tracking is overkill and has some overhead. Instead, look for QEnterEvent and QLeaveEvent - those should be sent to the editor widget without mouse tracking enabled. But still - do not intercept those via any subclassing!
Do note that QWidget is-a QObject, so the event filter can be a widget class, or a non-widget class.
I solved this problem like this
void Log::enterEvent(QEvent* event)
{
m_pButton->show();
}
void Log::leaveEvent(QEvent* event)
{
m_mousePos = this->mapFromGlobal(QCursor::pos());
QPoint logPos = mapToGlobal(this->normalGeometry().bottomLeft());
QPoint buttonPos = m_pButton->pos();
QPoint buttonRelatviePos = buttonPos - logPos;
// hide button when button is not in the button pos
if ((m_mousePos.x() < buttonRelatviePos.x()) or (m_mousePos.x() > buttonRelatviePos.x() + m_pButton->width()) or
(m_mousePos.y() < buttonRelatviePos.y()) or (m_mousePos.y() > buttonRelatviePos.y() + m_pButton->height()))
{
m_pButton->hide();
}
}

QWidget listening to other QWidgets events

To explain the situation, I have a QMainWindow with plenty of stuff inside.
My goal is to make some kind of a notification that appears at the bottom right corner of this window.
The notification widget must be over every other widgets.
So far I've created that Notification widget that inherits from QWidget.
By setting the QMainWindow as its parent I've got it 'floating' on top of every other widgets. It doesn't belong to any layout.
Now the big issue is to make it stick to the bottom right corner. I managed to place it manually but what I want now is to move it upon a resize event.
Unfortunately since it doesn't belong to any layout it doesn't receive any resizeEvent, so I can't overload the resizeEvent() function.
An attempt was to make the QMainWindow emit a signal in its resizeEvent method but I am not really happy with this method. Especially because in the Notification widget constructor I have this connect(static_cast<MainWindow*>(parent), &MainWindow::resized, this, &Notification::update_position);, and it kind of breaks the genericity of the widget since its parent must be a MainWindow widget.
So the question is how can one widget react to another widget event ?
In my case here how can the notification widget be notified when its parent is getting resized ?
And I forgot to mention that I don't want the MainWindow widget to know anything about this notification widget. The notification widget can be managed on its own. You just call something like new Notification(parent) and it will do its stuff on its own
Any ideas are welcome,
Thanks in advance :)
You can install the notification widget as an event filter for its parent, e.g.:
NotificationWidget::NotificationWidget(QWidget *parent) : QWidget(parent) {
parent->installEventFilter(this);
}
bool NotificationWidget::eventFilter(QObject *obj, QEvent *event) {
// normal event handling
bool ret = QObject::eventFilter(obj, event);
if (event->type() == QEvent::Resize) {
// Parent resized, adjust position
}
return ret;
}

Setting a QDialog to be an alien

In a standalone GUI application where I don't have a windowmanager nor a composite manager I want to display a QDialog to the user to ask for values.
The dialog is quite big, so I want to make it translucent so that the user can look through it to see what happens in the application while the dialog is shown.
The problem is that for translucency of X native windows, there needs to be a composite manager. Qt internal widgets can be painted translucent because they don't correspond to native X windows (aliens) and are completely only known to Qt.
Is there a way to make the background of a QDialog translucent without having a composite manager running? Perhaps making it a normal child widget/alien of the application's main window? Is there a better alternative to this?
I don't know of any way of turning a QDialog into a normal child widget. Looking at the Qt for X11 code, I can't figure out a way of not setting the Qt::WindowFlags passed to the QWidget (parent) constructor so that it would be a plain widget and not a window of its own (but I could be wrong, didn't spend a lot of time on that).
A simple alternative is to use a plain QWidget as your container, instead of a QDialog. Here's a sample "PopupWidget" that paints a half-transparent-red background.
#include <QtGui>
class PopupWidget: public QWidget
{
Q_OBJECT
public:
PopupWidget(QWidget *parent): QWidget(parent)
{
QVBoxLayout *vl = new QVBoxLayout;
QPushButton *pb = new QPushButton("on top!", this);
vl->addWidget(pb);
connect(pb, SIGNAL(clicked()), this, SLOT(hide()));
}
public slots:
void popup() {
setGeometry(0, 0, parentWidget()->width(), parentWidget()->height());
raise();
show();
}
protected:
void paintEvent(QPaintEvent *)
{
QPainter p(this);
QBrush b(QColor(255,0,0,128));
p.fillRect(0, 0, width(), height(), b);
}
};
To show it, call it's popup() slot which will raise it to the top of the widget stack, make it as large as its parent, and show it. This will mask all the widgets behind it (you can't interact with them with the mouse). It hides itself when you click on that button.
Caveats:
this doesn't prevent the user from using Tab to reach the widgets underneath. This could be fixed by toggling the enabled property on your "normal" widget container for example. (But don't disable the PopupWidget's parent: that would disable the popup widget itself.)
this doesn't allow for a blocking call like QDialog::exec
the widgets in that popup won't be transparent, you'd have to create custom transparent-background versions for all the widget types you need AFAIK.
But that's probably less of a hassel than integarting a compositing manager in your environment.

QGraphicsScene not responding to synthetic events

I have a very simple QGraphicsScene dervied class of my own:
class SceneComponent : public QGraphicsScene
{
Q_OBJECT
public:
explicit SceneComponent(QObject* parent = 0);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
};
And mousePressEvent(QGraphicsSceneMouseEvent*) is defined as:
void SceneComponent::mousePressEvent(QGraphicsSceneMouseEvent* event) {
Q_UNUSED(event);
std::cout<<"[Processing] MouseEvent"<<std::endl;
}
To display this QGraphicsView, this is as simple as:
QGraphicsView view(sceneComp);
view.show();
Now, when I click on the Window (for the QGraphicsView) that is displayed, I get the following output:
[Processing] MouseEvent
[Processing] MouseEvent
However, when I send a synthetic event using:
QMouseEvent* mevent = new QMouseEvent(
QMouseEvent::MouseButtonPress, QPoint(50, 50),
Qt::LeftButton, Qt::NoButton, Qt::NoModifier
);
QApplication::sendEvent(&view, mevent);
I get absolutely no output. Why is this?
On a related note, installing a eventFilter on the QGraphicsScene yields no results at all. This is probably (in my opinion) because of the fact that a QGraphicsScene expects a QGraphicsSceneMouseEvent instead of a QMouseEvent. This props up two questions:
Why is it so that a QGraphicsScene not accept standard QEvent and only QGraphicsSceneEvent?
Why is any QGraphicsSceneEvent derived class not instantiable?
Why is that I can send events to a QGraphicsScene using the sceneEvent method, but I need to specify a specific QGraphicsItem to send the event to?
From the docs for QGraphicsSceneEvent:
When a QGraphicsView receives Qt
mouse, keyboard, and drag and drop
events (QMouseEvent, QKeyEvent,
QDragEvent, etc.), it translates them
into instances of QGraphicsSceneEvent
subclasses and forwards them to the
QGraphicsScene it displays. The scene
then forwards the events to the
relevant items.
For example, when a QGraphicsView
receives a QMouseEvent of type
MousePress as a response to a user
click, the view sends a
QGraphicsSceneMouseEvent of type
GraphicsSceneMousePress to the
underlying QGraphicsScene through its
mousePressEvent() function. The
default
QGraphicsScene::mousePressEvent()
implementation determines which item
was clicked and forwards the event to
QGraphicsItem::mousePressEvent().
QGraphicsView uses a viewport widget to display the contents of the scene and to receive user events. Try sending your event to the QGraphicsView::viewport() widget - it should work.
By design - see the quote, these events are probably not meant to be sent manually. QMouseEvent is associated with a physical action - ex. mouse click, while QGraphicsSceneMouseEvent is something abstract...
The question is not clear, but that quote might be helpful too. Which event in particular are you talking about? You should also keep in mind that QGraphicsScene has QObject level events, sent with QApplication::sendEvent(), and QGraphicsItem events, that are sent with QGraphicsScene::sendEvent(). These two kinds work in different planes and have different purposes. Refer to the docs for more info.

QGraphicsItem doesn't receive mouse hover events

I have a class derived from QGraphicsView, which contains QGraphicsItem-derived elements. I want these elements to change color whenever the mouse cursor hovers over them, so I implemented hoverEnterEvent (and hoverLeaveEvent):
void MyGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
update (boundingRect());
}
However, this event handler code is never executed. I've explicitly enabled mouse tracking:
MyGraphicsView::MyGraphicsView(MainView *parent) :
QGraphicsView(parent)
{
setMouseTracking(true);
viewport()->setMouseTracking(true);
...
}
Still, no luck. What am I doing wrong?
Fixed it. I need to use setAcceptHoverEvents(true) in the constructor of my QGraphicsItem-derived class.
In my case, hover events wouldn't work if I overrode mouseMoveEvent in my implementation of the QGraphicsView class. I fixed this by adding a call to
QGraphicsView::mouseMoveEvent(event);
which propagated the event to the parent, which in turn sent it out to all the scene items.