I have a custom QGraphicsScene in which I have a mouseMoveEvent(QGraphicsSceneMouseEvent *event);
When I hover on the scene with the mouse, the mouseMoveEvent gets properly fired. However when I hover with a mouse button pressed, then it does not get fired anymore.
This is how I setup the whole scene in the main window:
scene = new NodeScene(this); -> My Custom QGraphicsScene class
scene->setSceneRect(QRectF(0, 0, 5000, 5000));
QHBoxLayout *layout = new QHBoxLayout;
view = new QGraphicsView(scene);
layout->addWidget(view);
view->setDragMode(QGraphicsView::RubberBandDrag);
view->setMouseTracking(true);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
scene->setCentralWidget(widget);
And here is the code where I do handle mouse events (it's for Maya execution):
void NodeScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
MGlobal::displayInfo("Move");
QGraphicsScene::mouseMoveEvent(event);
}
void NodeScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
MGlobal::displayInfo("Press");
QGraphicsScene::mousePressEvent(event);
}
void NodeScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
MGlobal::displayInfo("Release");
QGraphicsScene::mouseReleaseEvent(event);
}
Any idea how I can get the mouseMoveEvents even when a mouse button is pressed ?
It sounds like you're not implementing all of the mouse events, just the mouseMoveEvent.
When overriding a mouse event, you should handle all of them (move, press and release events).
You can then set a boolean in the press event to know whether or not the mouse button is held down when entering the mouseMove event.
Found the issue by comparing my code to other examples, and it is due to the setDragMode(QGraphicsView::RubberBandDrag);
line. The code should by default be on QGraphicsView::NoDrag and the RubberBandDrag be enabled only upon press.
Related
I am creating a video application.
Upon starting this app, you would see a VideoWidget looping a playlist along with other widgets in the screen. By clicking the VideoWidget, the VideoWidget will go to fullscreen mode and a volume slider will be laid over it. It would go to show normal if clicked again in fullscreen mode.
To do this I created 2 classes. First, I created a main class that would contain all widgets including the Video widget. Second, I created a custom VideoWidget class. I instatiated my Qslider in this VideoWidget class and I instantiated a VideoWidget Object in my main class whose object is instantiated in main.cpp.
I got what I expect it to do. Except that the slider would not update its position immediately. It would only update position if you click to show normal then click to go back fullscreen. The volume change but the position of slider in UI does not change while in fullscreen.
I would like to ask what am I doing wrong? What should I do so that the slider position would update in UI?
Code Snippet:
in VideoWidget.h
class VideoWidget : public QVideoWidget
{
Q_OBJECT
QVideoWidget* videoWidget;
QMediaPlaylist* playlist;
QMediaPlayer *player;
public:
VideoWidget();
QSlider* slider;
};
In VideoWidget.cpp
VideoWidget::VideoWidget()
: videoWidget(new QVideoWidget(this)),
slider(new QSlider(Qt::Horizontal, this))
{
/*QMediaplaylist *playlist, QMediaPlayer *player instantiated here*/
slider->hide();
slider->setGeometry(300,735,600,20);
slider->setRange(0, 100);
slider->setValue(player->volume());
connect(slider, &QSlider::valueChanged, player, &QMediaPlayer::setVolume);
}
void VideoWidget::changeEvent(QEvent *event)
{
if(event->type() == QEvent::WindowStateChange)
slider->setVisible(windowState() == Qt::WindowFullScreen);
QWidget::changeEvent(event);
}
enter code here
void VideoWidget::resizeEvent(QResizeEvent* event) {
videoWidget->resize(size());
event->accept();
}
void VideoWidget::mousePressEvent(QMouseEvent *event)
{
this->setFullScreen(!isFullScreen());
event->accept();
}
In the MainWidget.cpp
mainwidget::mainwidget(QWidget *parent)
: QWidget(parent)
{
videoWidget = new VideoWidget(); // the video container
videoWidget->setFixedSize(500, 300);
QBoxLayout *displayLayout = new QHBoxLayout;
displayLayout->addWidget(videoWidget, 2);
QBoxLayout *layout = new QVBoxLayout;
layout->addLayout(displayLayout);
setLayout(layout);
videoWidget->setGeometry(100,100,300,400);
videoWidget->show();
}
edit:
This is the app at startup playing a video of my hand.
When I click the Video,
The video sets to fullscreen and the slider appears. The slider can control the volume of mediaplayer but the problem is, it won't move when dragged.
You're very confusing in ways you describe what the problem is, but if I get you right that QSlider doesn't track the mouse but you can change volume with it, presumably with a click?
You had connected valueChangedsignal , which is emitted only after sliderReleased() if tracking property is false. You have to handle Pressed\Moved\Released group of signals if you want to adjust volume continuously, or you can use built-in function of QSlider (which usually is enough):
slider->setTracking(true);
I have created a set of wizard pages inherited from qwizardpage. in one of my wizard page i have a graphics view and in that using graphics scene and a qrect a rectangle has been created and now i am trying to catch the callback event of mouse move when the mouse is in the graphicsview area, but outside this area only call back is happening. But mouse press event is functioning properly. could anyone suggest me any solution.
WizardPage::WizardPage(QWidget* parent)
{
m_pScene = new QGraphicsScene(this);
graphicsView->setScene(m_pScene);
m_pRect = new QRect(-25, 25, 100, 40);
m_pScene->addRect(*m_pRect);
setMouseTracking(true);
}
void EgoLeverWizardPage::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << "move";
}
In Windows when I create a QMainWindow I can move it around the screen by clicking the title bar and dragging it.
In my application I've hidden the title bar by using setWindowFlags(Qt::CustomizeWindowHint) and I'm trying to build a custom title bar using a widget and setting it in the menu space with setMenuWidget(myWidget).
Now I want to reproduce the original behaviour: I want to click on my MyWidget widget inside the QMainWindow and, while mouse is pressed, dragging the mouse moves the window.
Is there a way to do it?
This is an example on how to implement a fake title bar, that has standard buttons (minimize, maximize, close), and can be dragged to move the whole window (this is based on the approach in #Kevin's answer).
#include <QtWidgets>
class FakeTitleBar : public QWidget{
Q_OBJECT
public:
explicit FakeTitleBar(QWidget* parent= nullptr):QWidget(parent){
label.setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
layout.addWidget(&label);
layout.addWidget(&buttonMinimize);
layout.addWidget(&buttonMaximize);
layout.addWidget(&buttonClose);
//connecting buttons' signals to slots
connect(&buttonMinimize, &QPushButton::clicked,
this, &FakeTitleBar::MinimizeWindow);
connect(&buttonMaximize, &QPushButton::clicked,
this, &FakeTitleBar::MaximizeWindow);
connect(&buttonClose, &QPushButton::clicked,
this, &FakeTitleBar::CloseWindow);
//setting vertical fixed size policy
//so that the title bar does not take up any additional space
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
//a bit of styling
setStyleSheet("QPushButton {margin:0px; padding:5px;}"
"QWidget {background-color:blue; color:white;}");
}
public slots:
//slots for corresponding buttons
void MinimizeWindow(){
window()->showMinimized();
}
void MaximizeWindow(){
if(!window()->isMaximized())
window()->showMaximized();
else
window()->showNormal();
}
void CloseWindow(){
window()->close();
}
protected:
void mousePressEvent(QMouseEvent* event){
//save the press position (this is relative to the current widget)
pressPos= event->pos();
isMoving= true;
}
void mouseMoveEvent(QMouseEvent* event){
//isMoving flag makes sure that the drag and drop event originated
//from within the titlebar, because otherwise the window shouldn't be moved
if(isMoving){
//calculate difference between the press position and the new Mouse position
//(this is relative to the current widget)
QPoint diff= event->pos() - pressPos;
//move the window by diff
window()->move(window()->pos()+diff);
}
}
void mouseReleaseEvent(QMouseEvent* /*event*/){
//drag and drop operation end
isMoving= false;
}
//double-clicking on the title bar should maximize the window
void mouseDoubleClickEvent(QMouseEvent* /*event*/){
MaximizeWindow();
}
//in order for the style sheet to apply on this custom widget
//see https://doc.qt.io/qt-5/stylesheet-reference.html#qwidget-widget
void paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
private:
QHBoxLayout layout{this};
QLabel label{"Fake Title Bar"};
QPushButton buttonMinimize{"-"};
QPushButton buttonMaximize{"M"};
QPushButton buttonClose{"X"};
QPoint pressPos;
bool isMoving{false};
};
//sample usage
class Widget : public QWidget{
public:
explicit Widget(QWidget* parent= nullptr):QWidget(parent){
setWindowFlags(Qt::CustomizeWindowHint);
layout.addWidget(&titleBar);
layout.addWidget(&label);
layout.setContentsMargins(0, 0, 0, 0);
label.setAlignment(Qt::AlignCenter);
//default size for the window
resize(320,240);
}
~Widget(){}
private:
QVBoxLayout layout{this};
FakeTitleBar titleBar;
QLabel label{"this is a sample window"};
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
#include "main.moc"
You just need to implement the necessary mouse event handling by overwriting MyWidget's mousePressEvent(), mouseMoveEvent() and mouseReleaseEvent() handlers.
Detect the mouse down, get current mouse position
While moving, get current mouse position, calculate difference, save new position, move window by diff
You can get the window (top level widget) from inside MyWidget through the window() method.
I have a QGraphicsView inside the MainWindow that has implemented a QGraphicScene. I need to pop up a widget when I right click the mouse on a certain portion of the QGraphicScene. The parent of the widget needs to be MainWindow.
My problem is that I need to verify the validity of the portion on witch I clicked inside a mousePressEvent in QGraphicScene and to pop up the widget at the exact same location but the coordinates of the QGraphicScene and MainWindow are obvious not the same. For that I use a custom signal that trigger a slot inside MainWindow and get the coordinates from the mousePressEvent of the MainWindow. The problem is that the mouseEvent from the QGraphicsScene is triggered before the mouseEvent from MainWindow. This makes perfect sense and works if I right click twice but I need it to work from the first right click.
I can not implement a filter or change the focus because I have tons of events in the application.
QGraphicScene:
void CGraphicScene :: mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event -> button() == Qt::RightButton)
{
//test stuff
emit signalChangeContextualMenuPosition();
m_contextualMenu -> show();
}
}
MainWindow:
CGraphicScene *scene;
CContextualMenu *m_contextualMenu;
m_contextualMenu = new CContextualMenu(this);
m_contextualMenu ->close();
scene = new CGraphicScene(m_contextualMenu);
ui->gvInterface -> setScene(scene);
connect(scene, SIGNAL(signalChangeContextualMenuPosition()), this, SLOT(openPopUp()));
void MainWindow :: openPopUp()
{
m_contextualMenu ->move(m_xCoordPopMenu, m_yCoordPopMenu);
}
void MainWindow :: mousePressEvent(QMouseEvent *event)
{
if(event -> button() == Qt::RightButton)
{
m_xCoordPopMenu = event -> x();
m_yCoordPopMenu = event -> y();
}
}
Use the QGraphicsView::mapFromScene() to map scene coordinates to the view widget coordinates and then QWidget::mapToParent() to map the coordinates to it's parent widget, which is probably your main window. You can also find useful the method QWidget::mapTo().
Hello in my application i'm making a QGraphicsView and set the scene on it:
QGraphicsProxyWidget *rotateItemIcon;
HoverFilter *hv = new HoverFilter(); // my hover filter class
connect(hv,SIGNAL(SignalHover(QObject*)),this,SLOT(ObjectHover(QObject*)));
connect(hv,SIGNAL(SignalHoverLeave(QObject*)),this,SLOT(ObjectHoverLeave(QObject*)));
ui->TestIcon->installEventFilter(hv);
...
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 661, 255);
ui->TestIcon->setParent(NULL);
rotateItemIcon = scene->addWidget(ui->TestIcon); // here i add my control to the scene and receive QGraphicsProxyWidget object
rotateItemIcon->setTransformOriginPoint(ui->TestIcon->width()/2,
ui->TestIcon->height()/2);
ui->graphicsViewFive->setScene(scene); //QGraphicsView on my form
ui->graphicsViewFive->show();
my HoverFilter.cpp
#include "hoverfilter.h"
#include "QDebug"
HoverFilter::HoverFilter()
{
}
bool HoverFilter::eventFilter( QObject *dist, QEvent *event )
{
if( event->type() == QEvent::Enter )
{
emit SignalHover(dist);
return true;
}
if( event->type() == QEvent::Leave )
{
emit SignalHoverLeave(dist);
return true;
}
return false;
}
rotateItemIcon is my QGraphicsProxyWidget and the problem is that it has weird boundaries, i need to implement some animation on hover of my control TestIcon, (i done that using event filter) mouse enter and mouse leave fires when i drag my mouse on a random places, not only on my TestIcon control. If do not add my control to the QGraphicsScene hover detection works fine, so i assume this is a scene/proxywidget problem. Is there a way i can set size or boundaries to QGraphicsProxyWidget to stop that?
I'm not sure if I completely understand your question, but it sounds like you're have a widget placed in a scene via a QGraphicsProxyWidget and when you try to detect if your mouse is over the widget you want it to animate.
By 'weird boundaries' I assume you mean the extents to which the proxy widget is accepting the mouse as being over the widget. If so, I suggest either calling setGeometry on the QGraphicsProxyWidget to set its position and dimensions, or inherit from QGraphicsProxyWidget and implement the boundingRect function to define the area of the proxy widget.