I have in my UI one QScrollArea with an image, and I want to get some value when I click on the image.
Being more explict, I need to change the brightness of an image, and I will get the value with the mouse. I already see the MouseMoveEvent but I don't know how to use it.
If I get the position of the mouse when I click and drag, I can extract one value to change the bright of my image, that I know. I just don't know how I will get the position.
Does anyone know how I can do this?
Ps.: My QScrollArea was created on Design, so I don't have any code writed by me with the specifications of the QScrollArea.
there is a small correction . when i programmed like this i was getting the movements a liitle jerky . so i tried to modified the code and now working perfect .
heres how i changed
const int deltaX = event->scenePos().x() - m_lastClickPosition.x();
if ( deltaX > 10 )// to the right
{
moveBy(10,0);
m_lastClickPosition = event->scenePos();
}
else if ( deltaX <-10 )
{
moveBy(-10,0);
m_lastClickPosition = event->scenePos();
}
All the information you need is in the QMouseEvent object that is sent to the mouseMoveEvent handler of your widget.
QMouseEvent::buttons()
QMouseEvent::pos()
A simple way to do what you're after is to change the brightness of the image whenever you receive a "mouse movement event" and the QMouseEvent object reports a button down (this means the user is moving the mouse while holding down a button).
void MyWidget::mousePressEvent( QMouseEvent* event )
{
if ( event->button() == Qt::LeftButton )
{
// Keep the clicking position in some private member of type 'QPoint.'
m_lastClickPosition = event->pos();
}
}
void MyWidget::mouseMoveEvent( QMouseEvent* event )
{
// The user is moving the cursor.
// See if the user is pressing down the left mouse button.
if ( event->buttons() & Qt::LeftButton )
{
const int deltaX = event->pos().x() - m_lastClickPosition.x();
if ( deltaX > 0 )
{
// The user is moving the cursor to the RIGHT.
// ...
}
else if ( deltaX < 0 ) // This second IF is necessary in case the movement was all vertical.
{
// The user is moving the cursor to the LEFT.
// ...
}
}
}
Related
I would like to retain the ability of a user to zoom and drag a QGraphicsScene, thus I cannot simply lock the QGraphicsView.
However a user should not be able to drag a QGraphicsItem out of the scenes viewport. Therefore I am looking for a way to interrupt a MouseDragEvent without ignoring the DragMoveEvent (aka have the QGraphicsItem jump back to its origin point). I have tried to accomplish this behaviour using the releaseMouse()-function but that didn't work at all. Any suggestions?
Thanks!
When dealing with qt graphics scene view frame work and dragging, it is better to re-implement QGraphicsItemand::itemChange than dealing with mouse directly.
This is the function defined in header file:
protected:
virtual QVariant itemChange( GraphicsItemChange change, const QVariant & value );
Then in the function, you detect the position change, and return the new position as needed.
QVariant YourItemItem::itemChange(GraphicsItemChange change, const QVariant & value )
{
if ( change == ItemPositionChange && scene() )
{
QPointF newPos = value.toPointF(); // check if this position is out bound
{
if ( newPos.x() < xmin) newPos.setX(xmin);
if ( newPos.x() > xmax ) newPos.setX(xmax);
if ( newPos.y() < ymin ) newPos.setY(ymin);
if ( newPos.y() > ymax ) newPos.setY(ymax);
return newPos;
}
...
}
Something like this, you get the idea.
I created my own classes (view and scene) to display image and objects I added to it, even got zoom in/out function implemented to my view, but now I have to add new functionality and I don't even know how to start looking for it.
Whenever I press the scroll button of my mouse and hold it - I wish to move around the scene, to see different parts of it - just like I would with sliders. It is supposed to be similar to any other program allowing to zoom in/out to image and move around zoomed picture to see different parts of it.
Unfortunately - I don't even know how to look for some basic, because "moving" and similar refer to dragging objects around.
EDIT 1
void CustomGraphicView::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::MidButton)
{
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
translate(event->x(),event->y());
}
}
Tried this - but it is working in reverse.
I suppose you know how to handle events using Qt.
So, to translate (move) your view use the QGraphicsView::translate() method.
EDIT
How to use it:
void CustomGraphicsView::mousePressEvent(QMouseEvent* event)
{
if (e->button() == Qt::MiddleButton)
{
// Store original position.
m_originX = event->x();
m_originY = event->y();
}
}
void CustomGraphicsView::mouseMoveEvent(QMouseEvent* event)
{
if (e->buttons() & Qt::MidButton)
{
QPointF oldp = mapToScene(m_originX, m_originY);
QPointF newP = mapToScene(event->pos());
QPointF translation = newp - oldp;
translate(translation.x(), translation.y());
m_originX = event->x();
m_originY = event->y();
}
}
I'm building a very simple image editor on Qt creator.I have my image displayed on a QGraphicsView and i want to give the user the ability to zoom in and out by a pushbutton.
I've searched a lot and found how to zoom in and out through the mouse wheel.As i am very new to Qt i can't adjust it to the pushbutton because i don't understand everything clearly.
I' ve tried this(without understanding completely what i'm doing)but the result isn't the wanted.It zooms in only once and quite abruptly.I want a smoother zoom and as many times as i want.
void MainWindow::on_pushButton_clicked(){
QMatrix matrix;
ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
matrix.scale(1.0,1.0);
ui->graphicsView->setMatrix(matrix);
ui->graphicsView->scale(1,-1);
}
I would be very grateful if you guys can help
Below is how I implemented zooming in my subclass of QGraphicsView. Note that you'd need to pass in different values of "zoom" to get different magnifications as the zoom factor is an absolute value, not a relative one.
(The optMousePos argument can be set to point to a QPoint indicating the spot that should be the central-point of the zoom transformation, or it can be left NULL if you don't care about that. I use it because I zoom in and out based on the user turning the wheel in his mouse, and when doing that, the user usually wants to zoom in towards the point where his mouse point is currently positioned, rather than in towards the center of the graphics area)
qreal _zoom = 0.0;
[...]
void MyQGraphWidgetSubclass :: SetZoomFactor(qreal zoom, const QPoint * optMousePos)
{
if ((zoom != _zoom)&&(zoom >= 0.02f)&&(zoom <= 1000000.0f))
{
QPointF oldPos;
if (optMousePos) oldPos = mapToScene(*optMousePos);
// Remember what point we were centered on before...
_zoom = zoom;
QMatrix m;
m.scale(_zoom, _zoom);
setMatrix(m);
if (optMousePos)
{
const QPointF newPos = mapFromScene(oldPos);
const QPointF move = newPos-*optMousePos;
horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value());
verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value());
}
}
}
void MyQGraphWidgetSubclass :: wheelEvent(QWheelEvent* event)
{
QPoint pos = event->pos();
SetZoomFactor(_zoom*pow(1.2, event->delta() / 240.0), &pos);
event->accept();
}
I am currently developing on an image viewer application. In this application I have a so called "pan-zoom" feature. This means that, when holding a certain mouse button, the user can zoom the image by panning forth and back.
It works fine, but as the feature is used, the mouse (naturally) moves up and down on the screen and will at some point reach the screen borders, which will make it stop. Instead I would like a behaviour where the mouse remains stationary and only the image magnification changes.
I tried to achieve this by invoking QCursor::setPos inside the QWidget::mouseMoveEvent and reset the mouse to the initial position after I have processed the move. It works so far as that the mouse is staying nearly stationary (it's wiggling forth and back). However, this will cause the mouse move event to be called again effectively annulling the adjustment I just made. This will result in a "wiggling" effect. Every adjustment will immediately be reversed.
Here is a code snipped, so you get an idea of what I am doing:
void ImageView::mouseMoveEvent(QMouseEvent *e) {
//some code
if (_panZooming) {
//some code here
//doesn't work as expected because it invokes this event again
QCursor::setPos(mapToGlobal(_initialMousePosition.toPoint()));
}
}
Is there a way to prevent the mouse move event to happen when using QCursor::setPos?
Assuming you're not calling the base class mouseMoveEvent, you should accept the event to mark it as being handled. By default, they're accepted when you re-implement the event, but it's clearer to be explicit. Call e->accept( ).
It's also recommended that if you handle any of the mouse events, you should handle all, with the possible exception of mouse double click.
Here's an example of keeping the mouse still, though on OS X there's an occasional flicker which appears to be due to how Qt is handling the events
class MyWidget : public QWidget
{
void mousePressEvent(QMouseEvent* e)
{
m_pos = e->globalPos();
m_lastPos = m_pos;
QWidget::mousePressEvent(e);
}
void mouseMoveEvent(QMouseEvent* e)
{
// Calculate relative zoom factor
// scaled down ( / 10 ) for image zooming
m_zoomFactor += ((float)e->globalPos().y() - m_lastPos.y()) / 10;
QCursor::setPos(m_pos);
m_lastPos = m_pos;
e->accept();
qDebug() << m_zoomFactor << endl;
}
void mouseReleaseEvent(QMouseEvent* e)
{
QWidget::mouseReleaseEvent(e);
}
private:
QPoint m_pos;
QPoint m_lastPos;
float m_zoomFactor = 0; // C++ 11 initialisation
};
If you're not bothered at keeping the mouse stationary, take out the QCursor::setPos call and this will still receive move events when the cursor is outside the widget, whilst the mouse button is held down.
However, it may be a better user experience hiding the cursor when zooming.
I would have a flag to disable the event with will be false by default.
inside the event check if flag is false, then perform the zoom operation, set flag to true and reset cursor.
then the event will be called again and the flag will be true, so you set flag to false and you will be ready to handle the next event.
You just have to make sure you dont have two or more calls to the mouse event firing from the actual mouse before receiving the event from the setCursor call.
Don't use event->pos() in mouse events, use QCursor::pos() intead and check if it changed. Like this:
void MyWidget::mousePressEvent(QMouseEvent *)
{
mPrevPos=QCursor::pos();
mMoving=false;
}
void MyWidget::mouseMoveEvent(QMouseEvent *)
{
auto cursorPos=QCursor::pos();
if(mPressedPos==cursorPos){
return;
}
if(!mMoving
&& (cursorPos-mPrevPos).manhattanLength()>QApplication::startDragDistance()){
mMoving=true;
}
if(mMoving){
auto diff=cursorPos-mPrevPos;
// move something using diff
QCursor::setPos(mPrevPos);
}
}
void MyWidget::mouseReleaseEvent(QMouseEvent *)
{
mMoving=false;
}
void MyWidget::leaveEvent(QEvent *)
{
mMoving=false;
}
I move sprites stored in a formationvector by holding the mousebutton. Problem is: Whenever i move the sprite onto another one, i move both sprites at the same time.
What i want: Moving the sprite1 over the other sprites2 without changing the position of sprite 2 or with other words:
-Testing in a loop if a sprite of a vector is clicked on
-If sprite is clicked on:
Move this sprite around while button is pressed but somehow stop this move while this movement in order to avoid moving more than this one sprite.
Here is my try so far:
while (App.pollEvent(Event))
{
// Window closed
if (Event.type == sf::Event::Closed)
{
return (-1);
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
for (size_t k = 0; k < formation.size(); k++)
{
if (isMouseOver(formation[k], App) == true)
{
Mouseposition = sf::Vector2f(sf::Mouse::getPosition(App));
Mouseposition.x = Mouseposition.x - formation[k].getLocalBounds().width / 2;
Mouseposition.y = Mouseposition.y - formation[k].getLocalBounds().height / 2;
formation[k].setPosition(sf::Vector2f(Mouseposition));
Formation_playernames.clear();
Formation_playerinformation.clear();
Formation_Playernames(Font, Formation_playernames, formation, playerlist);
Formation_Playerinformation(Font, Formation_playerinformation, formation, playerlist);
}
}
}
}
The Formation-functions store the correct new positions of the sprites and their colors based on the positions into the formation vector
The problem is the for-loop, i could do it without but that would lead to a much longer code. How could i do it ?
Instead of simply checking whether or not the mouse button is pressed, you should structure your code so you can check exactly when the button is pressed, and when it is released. You can then structure your code such that different presses of the button are distinguished from each other. semi-pseudocode follows
bool button_is_pressed = false;
Sprite* currently_selected_sprite = nullptr;
// main application loop
while (...)
{
...
// other application logic
...
if (!button_is_pressed)
{
if (CheckIfButtonIsPressed())
{
button_is_pressed = true;
// Button was just pressed.
// Select the appropriate sprite by checking
// the mouse coordinates against the positions
// of the sprites.
}
else
{
// Button not being pressed.
// Likely no logic needed here.
}
}
else // button_is_pressed == true
{
if (CheckIfButtonIsPressed())
{
// Button is being held down.
// Implement dragging logic using the
// pointer to the selected sprite.
}
else
{
button_is_pressed = false;
// Button was just released.
// Deselect the sprite.
currently_selected_sprite = nullptr;
}
}
}
Alternatively, you could handle mouse events, which will take care of much of this logic for you. http://sfml-dev.org/documentation/2.0/classsf_1_1Event.php
In a more English like pseudo code, this is what your function is doing:
if the mouse button is currently pressed
move all the sprites which are under the mouse to be centered on the mouse cursor
else
do nothing
This is what I'm suggesting
at the moment the mouse button is pressed down
select the sprite which is under the mouse cursor
at the moment the mouse button is released
deselect the selected sprite
if the mouse button is currently pressed, and a sprite is selected
move the selected sprite to the position of the mouse cursor