I have a code I want to load every time I move my mouse cursor; it draws line to direction of mouse and beyond, and every time it the line gets to certain x coordinate, the line reflects. The problem is that now the program only draws line when I click on the PaintBox.
Here is my code so far:
void __fastcall TForm2::PaintBox1Click(TObject *Sender)
{
Form2->Refresh();
TPoint P;
::GetCursorPos( &P );
P = ScreenToClient( P );
int XX;
int YY;
if (P.x<240)
{
XX=15;
YY= ((445-P.y)*(XX-P.x)/(240-P.x)+P.y);
}
else if(P.x==240)
{
XX=240;YY=-5;
}
else
{
XX=465;
YY= ((445-P.y)*(XX-P.x)/(240-P.x)+P.y);
}
int delta=2*(445-YY);
this->Canvas->MoveTo(240, 445);
this->Canvas->LineTo(XX,YY);
while(0<YY&&YY<480&&YY!=445)
{
XX=abs(480-XX);
YY-=delta;
this->Canvas->LineTo(XX,YY);
}
}
You are trying to paint in an OnClick event handler. That's the wrong place to paint. The only correct place to paint to a VCL paint box is from its OnPaint event handler.
You will need to respond to OnClick though. Make a note of the location of the click and update any state that you need to maintain. Then call Invalidate on the paint box to force a paint cycle.
Related
I am working on a paint program in QT5 C++ and I am trying to modify a function that draws a line to incorporate the 45 degree, horizontal or vertical special line that should be drawn if the shift key is clicked.
Below is what I have but for some reason the key handler is not working for me.
I received an error but I don't understand what I need to do to fix it attached below is the error and the code for the paint function that I've modified after that. I have wrapped the modification I did in comments for readability
void LineInstrument::paint(ImageArea &imageArea, bool isSecondaryColor, bool)
{
QPainter painter(imageArea.getImage());
if(isSecondaryColor)
{
painter.setPen(QPen(DataSingleton::Instance()->getSecondaryColor(),
DataSingleton::Instance()->getPenSize() * imageArea.getZoomFactor(),
Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
}
else
{
painter.setPen(QPen(DataSingleton::Instance()->getPrimaryColor(),
DataSingleton::Instance()->getPenSize() * imageArea.getZoomFactor(),
Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
}
if(mStartPoint != mEndPoint) // here is where the line is drawn
{
painter.drawLine(mStartPoint, mEndPoint); // let the line be drawn
// my modifications start here
if (QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier) == true) { // check if shift key is active
QMouseEvent *mouse;
if (mouse->pos().x() > mouse->pos().y()){
// transform to a horizontal line
painter.save(); // save current painter state
painter.rotate(180);
painter.restore(); // restores painter state
}
else if (mouse->pos().x() < mouse->pos().y()){
// transfomr to a vertical line
painter.save();
painter.rotate(90);
painter.restore();
}
else{
// transform to a 45 degree line
painter.save();
painter.rotate(45);
painter.restore();
}
}// and end here
}
if(mStartPoint == mEndPoint)
{
painter.drawPoint(mStartPoint);
}
imageArea.setEdited(true);
// int rad(DataSingleton::Instance()->getPenSize() + round(sqrt((mStartPoint.x() - mEndPoint.x()) *
// (mStartPoint.x() - mEndPoint.x()) +
// (mStartPoint.y() - mEndPoint.y()) *
// (mStartPoint.y() - mEndPoint.y()))));
// mPImageArea->update(QRect(mStartPoint, mEndPoint).normalized().adjusted(-rad, -rad, +rad, +rad));
painter.end();
imageArea.update();
}
The error is pretty clear.
QMouseEvent *mouse; - you declare a pointer to the a QMouseEvent, but where is it instantiated? This is only a pointer which points to something.
If you want to handle mouse events you probably have to overload some kind of widget's mouse event (mouseMoveEvent, mousePressEvent, etc.). Those will provide you a valid QMouseEvent input.
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
Hi so I'm trying to make it so a little UFO bitmap (drawing/painting already taken care of) can be dragged around the screen. I can't seem to make the UFO position update and then redraw repeatedly from the MouseButtonDown() function (simplified code for mouse event handler). Any suggestions on detecting the dragging and redrawing accordingly? Code is below for relevant functions:
void MouseButtonDown(int x, int y, BOOL bLeft)
{
if (bLeft)
{
while(_bMouseMoving == true && _bMouseDragRelease == false) {
_iSaucerX = x - (_pSaucer->GetWidth() / 2);
_iSaucerY = y - (_pSaucer->GetHeight() / 2);
InvalidateRect(_pGame->GetWindow(), NULL, FALSE);
}
// Set the saucer position to the mouse position
_iSaucerX = x - (_pSaucer->GetWidth() / 2);
_iSaucerY = y - (_pSaucer->GetHeight() / 2);
}
else
{
// Stop the saucer
_iSpeedX = 0;
_iSpeedY = 0;
}
}
void MouseButtonUp(int x, int y, BOOL bLeft)
{
_bMouseDragRelease = true;
}
void MouseMove(int x, int y)
{
_bMouseMoving = true;
}
To clarify what chris said, you're only going to get the WM_xBUTTONDOWN message once, and you'll need to use that to toggle a dragging state that you can query when you recieve a WM_MOUSEMOVE message.
When you get the mouse move message during a dragging state, you'll want to invalidate the rectangle surrounding where the ufo was, and the rectangle surrounding where it is.
Invalidating a rectangle causes WM_PAINT messages, where you redraw whatever was behind the ufo, and the ufo in it's new place.
Or you could cheat and make the UFO a cursor when you're dragging :)
I'm using sfml. I need to write a function that will create a type sf::RectangleShape and return a new sf::RectangleShape with the position of the mouse at the current time and stay there after I use window.draw() Right now my code is:
sf::RectangleShape functions::Map(sf::RectangleShape wall0){
wall0.setPosition(0,0);
wall0.setSize(sf::Vector2f(200,200));
wall0.setFillColor(sf::Color::Green);
wall0 = this->wall0;
return wall0;
}
void functions::Draw(){
window.clear();
window.draw(logo);
if (menu){
window.draw(menuStart);
window.draw(menuOptions);
window.draw(menuExit);
}
if (playing){
void Map();
window.draw(wall0);
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)){
}
}
I want to return wall0 with the new mouse position when the left mouse button is pressed. I know how to get the mouse input but not how to draw the object and keep it on the window. I've gotten it to where it draws the object while the mouse button is clicked but it doesn't stay.
I'm not familiar with sfml - but briefly looking at your code this is what I think you want to do:
void functions::Map(sf::RectangleShape & wall0){
wall0.setPosition(0,0);
wall0.setSize(sf::Vector2f(200,200));
wall0.setFillColor(sf::Color::Green);
}
Then functions::Draw method:
...
if (playing){
window.draw( Map(wall00) );
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)){
...