graphicsview receives mouse event before the item - qt

I have implemented the panning view on the QGraphicsView, using the mouse move event using
void View::mouseMoveEvent(QMouseEvent* event) {
pan();
QGraphicsView::mouseMoveEvent(event);
}
and in the scene of this view I have added few items where some of the items are resizable, so I implemented
void Item::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(m_resizeMode)
{
resize();
e->accept();
}
}
I tried to filter the mouse move not to propagate to any further using e->accept()
but my View mouseMove event has been called first , so when ever I tried to resize the item, the view started to pan all the way.
How can I avoid this event propagation from view to scene.

You can call the base class implementation of the QGraphicsView and check if the event is accepted. However I would do this in the mousePressEvent instead of the mouseMoveEvent. After all this is where you determine if you should resize an item or do some panning. This is how I do something similar in my project:
void View::mousePressEvent(QMouseEvent *event)
{
...
QGraphicsView::mousePressEvent(event);
if(event->isAccepted())
move = false;
else
move = true;
}
void View::mouseMoveEvent(QMouseEvent *event)
{
if(!move)
{
QGraphicsView::mouseMoveEvent(event);
return;
}
... // you would do the panning here
QGraphicsView::mouseMoveEvent(event);
}
void View::mouseReleaseEvent(QMouseEvent *event)
{
if(!move)
{
QGraphicsView::mouseReleaseEvent(event);
return;
}
else
{
...
move = false;
}
QGraphicsView::mouseReleaseEvent(event);
}

The view will always receive the mouse event first.
So, in the view, check to see if the mouse is over an item before allowing it to pan by getting the mouse pos in scene coordinates and retrieving the items at that position with QGraphicsScene::items( )

Related

Is there a way to detect whether CollectionView scrolling has been initiated by the user or ScrollTo?

I have a page that hosts a CollectionView and Map.
there is a list of items, the ItemsSource of the CollectionView is set to this list and the map pins are drawn based on this list, there's a requirement that when the user scrolls the CollectionView the opposite map pin is highlighted, and when the pin is clicked the CollectionView is scrolled to that item.
I use ScrollTo and Scrolled event. but the problem is that when the ScrollTo is called the Scrolled event is fired too, and that causes a lag or some unexpected behavior.
I tried to set a flag:
private int centerIndex = -1;
bool userScroll;
private void CollectionView_Scrolled(object sender, ItemsViewScrolledEventArgs args)
{
if (centerIndex != args.CenterItemIndex)
{
if (userScroll)
MessagingCenter.Send<object, int>(this, Keys.GoToLocation, args.CenterItemIndex);
userScroll = true;
centerIndex = args.CenterItemIndex;
}
}
private void ScrollToVehicle(object arg1, Item item)
{
if (collectionView.ItemsSource != null && collectionView.ItemsSource.Cast<Item>().Contains(item))
{
userScroll = false;
collectionView.ScrollTo(item, position: ScrollToPosition.Center, animate: false);
}
}
but the problem is that Scrolled event is called multiple times (inside the if statement)
Try to unsubscribe from the CollectionView_Scrolled before call the ScrollTo
collection.Scrolled -= CollectionView_Scrolled;

qt mouse event filter

I have a QWidget with a QGraphicsView and a push button. The QGraphicsView has to take mouse press and release events to detect a swipe .At the same time push button should run a small function on clicked. I used an event filter in the QWidget to detect the mouse events.
bool Widget::eventFilter(QObject * obj, QEvent * event)
{
// Capturing keyboard events for moving
if( event->type() == QEvent::KeyPress )
{
//Do something
}
//Capturing mouse events for swipe
else if( event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*> (event);
swipe_startPoint = mouseEvent->pos();
}
else if( event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*> (event);
swipe_endPoint = mouseEvent->pos();
swipeDirection();
}
else
{
QWidget::eventFilter(obj,event);
}
}
In the constructor of the Widget class i have the following
ui->graphicsView->installEventFilter(this);
The problem is that the button is getting clicked but the MouseButtonRelease event that sets the 'swipe_endPoint' value is not working.
When i set
ui->graphicsView->grabMouse();
the mouse pressed and released events are working perfectly but the button stops accepting the events.
Other things that i have tried are :-
Set the event filter to the viewPort
ui->graphicView->viewPort()->installEventFilter(this);
Please help me get this mouse event working. Thanks in advance.
Note : Using QT 5.6.0 in Ubuntu

javafx Minesweeper: how to tell between right and left mouse button input

I am working on a javafx Minesweeper game, and currently am only using left mouse button input. I would like to use the right mouse button also so users can flag possible bombs. I looked at the Oracle webpage for Button class, and it says:
"When a button is pressed and released a ActionEvent is sent. Your application can perform some action based on this event by implementing an EventHandler to process the ActionEvent. Buttons can also respond to mouse events by implementing an EventHandler to process the MouseEvent."
https://docs.oracle.com/javafx/2/api/javafx/scene/control/Button.html
Ive tried this several different ways, with no success.
Included is my current EventHandler code. If anyone can explain the best way to handle right/left mouse clicks, or point me in the right direction of where to find that information, it is greatly appreciated.
MineButton is a custom class that extends Button. I would like on right click to mark as "m" and change cell color, and left click would remain the same.
for (int row = 0; row < 8; row++){
for (int col = 0; col <8; col++){
MineButton button = new MineButton(row, col);
button.setPrefSize(100, 100);
button.setText("?");
button.setStyle("-fx-font: 22 arial; -fx-base:#dcdcdc;");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if ( button.isAMine() == true){
button.setText("B");
for( int row1 = 0; row1 < 8; row1++){
for ( int col1 = 0; col1 < 8; col1++){
if (mineButtons[row1][col1].isAMine() == true){
mineButtons[row1][col1].setText("B");
mineButtons[row1][col1].setStyle("-fx- font: 22 arial; -fx-base: #dc143c;");
}
}
}
}
else{
recursion(mineButtons, button.getX(), button.getY());
}
}
});
You cannot handle right clicks on a button by attaching an event handler on action event. Instead you need to add a handler to the mouse event:
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if(mouseEvent.getButton().equals(MouseButton.SECONDARY)){
System.out.println("Set flag on the button");
}
}
});
If u wanna handle the MouseEvent use this code. It will work.
button.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if(event.getButton() == MouseButton.SECONDARY){
// Type code to set flag here
}
}
});
scene.setOnMousePressed(event ->{
if(event.getButton() == MouseButton.PRIMARY) {
anchorX = event.getSceneX();
anchorY = event.getSceneY();
anchorAngleX = angleX.get();
anchorAngleY = angleY.get();
}
});
scene.setOnMouseDragged((MouseEvent event) -> {
if(event.getButton() == MouseButton.PRIMARY) {
angleX.set(anchorAngleX - (anchorY - event.getSceneY()));
angleY.set(anchorAngleY - (anchorX - event.getSceneX()));
}
});
This code rotates a JavaFX group in a scene with a left mouse button and a drag. You can print the event.getButton to make certain your primary and secondary are working. I have tried many other ways including JavaFXtras. This is by far the simplest way to handle the primary and secondary mouse click events.

How to ensure, the entire area in QScrollArea was displayed?

I'm displaying some information to the user in QScrollArea.
The user should have seen all contents, before she can proceed (at least the content should have been scrolled to the end)
How could I detect this in an easily?
Is the reimplementing of virtual void scrollContentsBy (int dx,int dy) the only way?
EDIT
I was able to solve it, but had to use some workarounds:
Scroll-action value sent by the signal actionTriggered(int) had never the value QAbstractSlider::SliderToMaximum (Qt4.8, Windows 7). So I've checked manually, if the slider value is close to maximum.
Even if scroll-bar widget was dragged by mouse till the bottom, the value of the scroll-bar is never the maximum. Only if the scroll-bar widget is moved by any other event such as arrow-down or mouse wheel, the value may become maximum. I've work-arounded it with recheckPosition()
I hope, there are better solutions.
void NegativeConfirmation::recheckPosition()
{
processScrollAction(1);
}
void NegativeConfirmation::processScrollAction( int evt)
{
if ( evt == QAbstractSlider::SliderToMaximum) // Have not managed to receive this action
{
ui->bConfirm->setEnabled(true);
}
//Another approach
QWidget * sw = ui->scrollArea->widget();
if ( sw ) //any content at all ?
{
QScrollBar * sb = ui->scrollArea->verticalScrollBar();
if ( sb )
{
int sbv = sb->value();
int sbm = sb->maximum()-10;
if ( sbm>0 && sbv >= sbm )
{
ui->bConfirm->setEnabled(true);
}
else
{
QTimer::singleShot(1000, this, SLOT(recheckPosition()));
}
}
}
}
QScrollArea inherits QAbstractSlider which provides this signal: -
void QAbstractSlider::actionTriggered(int action)
Where action can be QAbstractSlider::SliderToMaximum.
I expect you can connect to the this signal and test when the action is QAbstractSlider::SliderToMaximum, representing that the user has scrolled to the bottom.

How to let QComboBox have context menu?

I have a Qt combo box. When it pops, items are listed down. When right clicking an item, I hope a context menu to pop up. Any way to implement it? I find a function onContextMenuEvent under QComboBox. Does it help? Thanks.
You can obtain the list widget using QComboBox::view. You can add a context menu to the list as usual. But also you should install event filter on the view's viewport and block right click events because such events cause popup list to close.
In the initialization:
QAbstractItemView* view = ui->comboBox->view();
view->viewport()->installEventFilter(this);
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(list_context_menu(QPoint)));
Event filter:
bool MainWindow::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::MouseButtonRelease) {
if (static_cast<QMouseEvent*>(e)->button() == Qt::RightButton) {
return true;
}
}
return false;
}
In the slot:
void MainWindow::list_context_menu(QPoint pos) {
QAbstractItemView* view = ui->comboBox->view();
QModelIndex index = view->indexAt(pos);
if (!index.isValid()) { return; }
QMenu menu;
QString item = ui->comboBox->model()->data(index, Qt::DisplayRole).toString();
menu.addAction(QString("test menu for item: %1").arg(item));
menu.exec(view->mapToGlobal(pos));
}
In this example items are identified by their displayed texts. But you also can attach additional data to items using QComboBox::setItemData. You can retrieve this data using ui->comboBox->model()->data(...) with the role that was used in setItemData.

Resources