SFML 2.1 - Detecting a single mouse click and changing a texture - c++

What I'm trying to do is:
I have a sprite. And this sprite has 3 textures. First texture, let's call it "not-visited", is the initial one. Now, when I click on the sprite it should change it texture to "clicked" meaning that the sprite was clicked. And this texture should remain until I click somewhere else on the screen. When I do this, the texture should change to the third one, "visited". And if I click on the sprite again it should change its texture to "clicked"...
So, I should have here a bool function called "ClickDetected" and its code should do this:
if (event.type == Event::MouseButtonReleased && event.mouseButton.button == Left)
if (mouse_over_sprite)
return true;
Right? OK, then, I have a class Node. This class has a method "Clicked":
bool Clicked {
if (ClickDetected) { return true; }
}
I know it may be unnecessary, but let it stay for now. Next, in Update() method of this class:
if (Clicked) { change_texture_to_"clicked"; if (!visited) visited=true; }
else {
if (!visited) change_texture_to_"not-visited";
if (visited) change_texture_to_"visited";
}
But this doesn't work how it should. When I click on sprite texture changes to "clicked". When I release the mouse button the "clicked" texture stays... But when I move the mouse texture changes to "visited". And it should remain "clicked", until I click somewhere else. I tried to use a while loop here, but it didn't work. What can I do?

I've put this code to Graph class (method Update()) to manage many Nodes. I've change it a little bit and now it works fine.

Related

How to set Direct Draw clipper according to window's client area position relative to the screen

I'm adding Direct Draw rendering option to my 2D Graphics Engine.
When setting the Direct Draw Clipper on a non fullscreen application clipper clips the client area with an offset if the Window's client area top left position is not on the 0,0 of the screen.
Here is my clipper setup code :
LPDIRECTDRAWCLIPPER directDrawClipper = nullptr;
if (!FAILED(mDirectDraw->CreateClipper(0, &directDrawClipper, NULL))) {
if (!FAILED(directDrawClipper->SetHWnd(0, App->getWindow()->getHandle()))) {
if (!FAILED(mDirectDrawFrontBuffer->SetClipper(directDrawClipper))) {
if (!FAILED(mDirectDrawBackBuffer->SetClipper(directDrawClipper))) {
return true; //all good
} else {
throw Error::Exception(L"DirectDraw arka görüntü bellek tamponu kesicisi kurulamadı",
L"Renderer Kurulum Hatası");
}
} else {
throw Error::Exception(L"DirectDraw ana görüntü bellek tamponu kesicisi kurulamadı",
L"Renderer Kurulum Hatası");
}
} else {
throw Error::Exception(L"DirectDraw kesicisi pencereye kurulamadı",
L"Renderer Kurulum Hatası");
}
} else {
throw Error::Exception(L"DirectDraw kesicisi kurulamadı",
L"Renderer Kurulum Hatası");
}
And here is the screenshot :
the size of the white areas are the distance of client area to the screen upper left corner.
Moving the window to upper left corner of the screen before setting the clipper and moving it back to original position doesn't help.
Thanks in advance.
For the curious I solved the problem with a hack.
Before setting the clipper move the window to a position which the client area of the window will be on 0,0 of the screen. You can use ClientToScreen function to determine the window position which client area will be on the 0,0 of the screen.
After setting up the DirectDraw move the window back to it's original position but there is a problem, if you move the window immediately after setting up the DirectDraw issue persists. (I believe clipper setting functions works async). To solve that you can set a windows timer , right after setting the DirectDraw set a timer (1 second got the job done in my case) and move window back to it's original position when the timer updates.
To summarize the process :
Move window to a new position which client area of the window will be on the 0,0 of the screen.
Set up the clipper
Set a timer (1 second worked in my case)
When timer updates move window back the it's original position.
But still if someone knows the proper solution it would be nice.

Constraining child QGraphicsItem to scene?

Does anyone have a better way to constrain a child of a QGraphicsItem to a scene?
I have successfully properly constrained a parent QGraphicsItem to its scene by overriding itemChange, but now I need to do the same for the child QGraphicsItem.
Example Use-case:
This code works... for the most part. The only problem is the QGraphicsItem's velocity when hitting either side will affect its endstop position:
QVariant SizeGripItem::HandleItem::itemChange(GraphicsItemChange change,
const QVariant &value)
{
QPointF newPos = value.toPointF();
if (change == ItemPositionChange)
{
if(scene())
{
newPos.setY(pos().y()); // Y-coordinate is constant.
if(scenePos().x() < 0 ) //If child item is off the left side of the scene,
{
if (newPos.x() < pos().x()) // and is trying to move left,
{
newPos.setX(pos().x()); // then hold its position
}
}
else if( scenePos().x() > scene()->sceneRect().right()) //If child item is off the right side of the scene,
{
if (newPos.x() > pos().x()) //and is trying to move right,
{
newPos.setX(pos().x()); // then hold its position
}
}
}
}
return newPos;
}
For the parent item, I used:
newPos.setX(qMin(scRect.right(), qMax(newPos.x(), scRect.left())));
which worked perfectly, but I'm stumped as to how or if I could use that here.
First, to be specific, scenes effectively have no boundaries. What you're trying to do is constrain the item to the scene rectangle that you've set elsewhere.
The problem I see is in your use of scenePos. This is an ItemPositionChange; the item's scenePos hasn't been updated with the new position yet, so when you check for scenePos being out of the scene rect, you're really checking the result of the last position change, not the current one. Because of that, your item ends up just off the edge of the scene rectangle and then sticks there. How far off the edge depends on how fast you were moving the mouse, which dictates how much distance there is between ItemPositionChange notifications.
Instead, you need to compare the new position to the scene rectangle and then restrict the value that gets returned to be within the scene rectangle. You need the new position in scene coordinates to do the comparison, so you need something like:
QPoint new_scene_pos = mapToScene (new_pos);
if (new_scene_pos.x() < scene()->sceneRect().left())
{
new_scene_pos.setX (scene()->sceneRect().left());
new_pos = mapFromScene (new_scene_pos);
}
This isn't complete code, obviously, but these are the conversions and checks you need to do to keep it in on the left side. The right side is very similar, so just use the new_scene_pos for the comparison there.
Note that I didn't assume that the left edge of sceneRecT is at 0. I'm sure that's what you coded where you set the sceneRect, but using the actual left value rather than assuming it's 0 eliminates any problems if you end up later changing the range of scene coordinates you're planning to work with.
I used "left" instead of "x" on the sceneRect call just because it parallels using "right" for the other side. They're exactly the same, but I think it reads slightly better in this case.

Qt - Adding a drawing to a Layout

I'm trying to make an application where the user can input some drawings (gestures) that will then be saved and displayed in a gallery on the top of the screen:
When the user presses "Validate", the drawing is supposed to be displayed on the scroll area on the top. However, for some reason, my code is not working the way I intended it to. It saves the drawing with no problem, but when I tell it to add it to the top, nothing happens.
Code here:
void MainWindow::galleryUpdate()
{
for (int i = 0; i < gestureTemplates.size(); i++)
{
QPolygonF scaledGesture = gestureTemplates[i].scaleToSquare(gestureTemplates[i]);
StrokeDrawer * strD = new StrokeDrawer();
QPalette Pal(palette());
Pal.setColor(QPalette::Background, Qt::white);
strD->setMinimumSize(50, 50);
strD->setAutoFillBackground(true);
strD->setPalette(Pal);
galleryList.append(strD);
topZone->addWidget(strD);
strD->setStroke(scaledGesture);
}
}
gestureTemplates is a vector of GestureTemplate (a custom class inheriting from QPolygonF) containing all the drawings. The first line inside the for simply scales the drawing to fit in a square, and returns a QPolygonF.
StrokeDrawer is the class used to display the drawing (code below). I then try to fill it with a white background, save it to galleryList which is a list of StrokeDrawer for each drawing, and then add it to the top, by using topZone->addWidget(strD), where topZone is a HBoxLayout.
I also use the setStroke method to set the drawing to the StrokeDrawer (this method also calls the update() function in the class, which calls its paintEvent, which should take care of actually drawing the QPolygonF).
Initially, I tried to do addWidget by directly using the QPolygonF but that didn't work. That's why I'm using this StrokeDrawer class (which just displays the drawing, it doesn't even allow making changes to it).
By debugging the code on QtCreator, everything works fine until the addWidget line, where nothing happens. I mean, even if the drawing is not being correctly displayed, it should at least show a small, white, 50x50 square, or am I missing something here?
Some code for StrokeDrawer:
void StrokeDrawer::setStroke(QPolygonF g)
{
gesture = g;
update();
}
void StrokeDrawer::paintEvent(QPaintEvent* e)
{
QWidget::paintEvent(e);
QPainter pait(this);
if (!gesture.empty())
pait.drawPolyline(gesture);
}

MFC - How to draw pixel waves inside a rectangle

I am new to MFC trying to learn from the scratch.
I am trying to create a small dialog based application in VS2012 - MFC.
On the dialog window i have drawn a rectangle (in OnPaint event) like this.
CPaintDC dc(this);
CRect background1(10,10,208,92);
dc.Rectangle(10,10,208,92);
Then i filled the bacground with some color. Like this.
CBrush brush1;
brush1.CreateSolidBrush(RGB(2,3,4));
dc.FillRect(background1,&brush1);
Now i wanted to draw waves in form of pixels continuously inside the rectangle.
As of now what i have done is,
bool top = false;
for(i=10,j=92;i<200 && j>10;)
{
pDC->SetPixel(i,j,NewColor);
Sleep(10);
if(!top)
j-=1;
else
j+=1;
if(j%4==0)
{
i+=1;
}
if(j==12)
top=true;
if(j==90)
top = false;
}
I am just drawing the pixels straightaway on the window, but within the dimensions where the rectangle lies. And the waves stop as it reaches the right dimension of the rect. But i feel thats not the right way to do it.
I want to draw the waves inside the rectangle and also continuosly, like when it reacahes the right end it should move left and it should be continuous.
Is there any proper way in MFC to draw something inside a rectangle (technically inside another object)?
Please help me out.
The ScrollWindow function can be used to scroll the existing graph to the left. Then you draw the new data to the right of the scrolled area. The drawing will be much faster if you use the Polyline function instead of DrawPixel: Polyline draws an array of pixels.
I tried your code and just added some condition, so that if it reaches right side it should start moving toward left.
void CMyDlg::OnBnClickedOk()
{
CWnd *cWnd = FromHandle(m_hWnd);
CDC *pDC = cWnd->GetDC();
bool top = false;
bool right = false;
int i,j;
for(i=10,j=92;i<=200 && j>10;)
{
pDC->SetPixel(i,j,RGB(255,255,255));
Sleep(10);
if(!top)
j-=1;
else
j+=1;
if(j%4==0)
{
if(!right)
i++; // if reaches right side i--;
else{
i--;
}
}
if(j==12)
top=true;
else if(j==90)
top = false;
if(i == 200)// make right = true if reaches right side
right = true;
else if(i == 10)// make false it reaches left side
right = false;
}
}
I am getting output something like this
NOTE: this will cause infinite loop, you need to check condition where you have to stop printing pixels.
You are NOT looping with Sleep(10) in your WM_PAINT handler, are you?
MFC or not, you should separate your data processing from presentation.
It appears you are trying to do an animation; the simplest way to do it is to have an off-screen (memory) DC and drawing your pixels on it. At certain times (frame rate?), you would call parent's InvalidateRect()/UpdateWindow() pair. Then in ON_PAINT, you would BitBlt() your prepared DC onto your preferred location in the dialog.

QGraphicsRectItem and QGraphicsScene problems at Scene change

what I want to do is the following:
I have a little GUI with a QGraphicsView. In this graphics View I load a picture:
// m_picture is QPixmap
// image is QImage
// m_graphic is QGraphicsScene
// graphicsView is QGraphicsView
m_picture.convertFromImage(image);
m_graphic->addPixmap(m_picture);
ui->graphicsView->setScene(m_graphic);
This doesn't cause any problems and I can always load a new image without problems.
Now in addition to just display the pictures I want to give the user the ability to draw a rectangle on them ( to "focus" on a specific area ). Actually the user just types in the coordinates in four text boxes on the GUI ( x,y, width,heigth). After providing the coordinates the User presses a button and the rectangle at the following coordinates shall be displayed.
I accomplished this with this code:
void tesseract_gui::show_preview_rect()
{
int x,y,h,w;
x = ui->numBox_x->value();
y = ui->numBox_y->value();
h = ui->numBox_h->value();
w = ui->numBox_w->value();
if( rect_initialized )
{
m_graphic->removeItem(m_rect);
}
else
{
rect_initialized = true;
}
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->addItem(m_rect);
return;
}
The remove call is because I always want to display just one rectangle.
Now as I mentioned this works fine. But if the user now loads another picture ( with the calls at the top of my post ) the program crashes when I try to draw a new rectangle. I
get a Segmentation fault at the call of
m_rect->setPen(QPen(Qt::red));
If I call
m_graphic->removeItem(m_rect);
after loading a new picture I get
QGraphicsScene::removeItem: item 0x8c04080's scene (0x0) is different from this scene (0x8c0a8b0)
and then it crashes with the same Error at setPen.
What I don't get is, I don't change the scene. I just add another picture to it ( or overwrite it) .
Well any suggestions how I could do this right?
Best Regards
// edit:
I tried to do it just with everytime a new rectangle like this:
void tesseract_gui::show_preview_rect()
{
int x,y,h,w;
x = ui->numBox_x->value();
y = ui->numBox_y->value();
h = ui->numBox_h->value();
w = ui->numBox_w->value();
m_graphic->clear();
m_graphic->addRect(x,y,h,w);
return;
}
Problem at this is that with the clear() call it also clears the picture itself from my GraphicsView... so no solution there
// edit:
As suggested I got rid of the Warning like this:
if( m_rect->scene() != 0 )
{
m_graphic->removeItem(m_rect);
}
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->addItem(m_rect);
I know it's not the best way but I tried it also this way ( did not work for me ):
I added the item in the constructor:
m_graphic->addItem(m_rect);
and then
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->update();
and I get the "same" error as always ( Program crashes at m_rect->setPen() )
So it seems the problem always occurs when I already added the rectangle to the graphic, THEN changed the image of m_graphic and then did any operation with m_rect. ( Actually I guess m_graphic takes ownership of m_rect and so this causes the segmentation fault ... ? )
The message QGraphicsScene::removeItem: item 0x8c04080's scene (0x0) is different from this scene (0x8c0a8b0) tells you m_rect is not in any scene at the time you call it. It's probably removed somewhere else in your code or you have 2 variables with the same name in the class hierarchy.
Also, you don't need to remove it from scene to change it. Just change it while it's in the scene. It will get repainted with the new color and geometry in the next paint event.
Even if you REALLY want to remove it before changing it, just check if it's in a scene by calling QGraphicsItem::scene(). There's no need for the init check variable.