GDI Win32 Plotting Graphs - c++

In my small, small and humble c++ Win32 GUI application, I got to the point of being able to draw a grid :
This is in preparation for displaying spectral data, the 36 (floats) measurement points returned from my device. I still have to get the axis labeled... This is my code for drawing the grid so far :
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, HBRUSH (COLOR_WINDOW + 1));
bool Retour;
pen = (HPEN)GetStockObject(TRANSPARENT);
SelectObject(hdc, pen);
Brosse = CreateSolidBrush(RGB(240, 240, 240));
SelectObject(hdc, Brosse);
Retour = LineTo(hdc, 100, 500);*/
Retour = Rectangle(hdc, 350, 150, 750, 375);
for (int i = 380; i <= 730; i=i+10) { // 36 vertical lines
MoveToEx(hdc, i, 175, NULL);
LineTo(hdc, i, 350);
}
for (int i = 175; i <= 350; i = i + 17) {
MoveToEx(hdc, 375, i, NULL);
LineTo(hdc, 730, i);
}
DeleteObject(pen);
DeleteObject(Brosse);
EndPaint(hWnd, &ps);
As you can see, I located the code to draw the grid inside the WM_PAINT procedure. It made sense to locate the code there to experiment with GDI instructions but also because the grid needs to be visible at launch time as part of the interface.
My question is how should I go "updating" the graph when, after pressing the "Measure Sample" button, I have access to the data? I gather that the code to draw on the grid does not necessarily needs to be located inside the WM_PAINT procedure, does it? According to my limited understanding, as long as I can get a Device Context handle (hDC), I should be good to go? All I need to plot is a "line" uniting the 36 data points from left to right. Not sure I should use simple MOVE_TO and LINE_TO for this purpose? I think there is a way to draw a "smoothed" line that passes through all 36 data points?
One last thing, if I may... I'm going at this in a very elementary way because I'm intimidated by the idea of using a "graphic library" and objects. But I suspect they would make my life a lot easier while offering umpteen options that will take me too long to figure out, I'll bet, and implement?
Thank you so much in advance for your kind help and patience.

You should always do all your drawing in your WM_PAINT handler. That way, the window will redraw correctly when something that was covering it is removed, for example.
When your graph data changes, call InvalidateRect to prompt a redraw.
To determine what to redraw, you will need to pass suitable variables to your WM_PAINT handler. Start with MoveTo and LineTo (or Polyline or PolyPolyline) and get that working first. You can then investigate smoothing algorithms if you think you need them.

You should place all of your custom painting code in the WM_PAINT handler of the window, yes. You can call a separate function for painting the graph if you like. Pass it the HDC you get from BeginPaint(...) and your spectral data. However, that function needs to be called from the WM_PAINT handler.
If you handle painting in WM_PAINT then to update the graph when the data changes you would call InvalidateRect(...) on the window. InvalidateRect tells Windows essentially "this window has changed and needs to be repainted". It will drive a call to the paint handler.
One problem you are going to run into is that your graph is going to flicker. It may not be too noticeable. It may not bother you, but the problem is that in each call to the WM_PAINT handler you are erasing the graph in the screen device context; this erasure will be visible. A solution to this problem would be to "double-buffer" the graph: create an offscreen bitmap, paint into that when data changes, and paint the offscreen bitmap to the screen in WM_PAINT. You could also make the graph a separate child window that manages the offscreen bitmap, etc., if you are ever going to need multiple graphs or just to add some modularity to the code.
As for whether you'd have an easier time with a graphics library ... possibly, but more to the point: you would have an easier time using a higher level GUI library than the Win32 API. If you only care about Windows, consider the .Net frameworks WinForms or WPF. Otherwise, consider Qt.

Related

How to clear the screen in Win32 api

I have a C++ windows API program which displays text using TextOut function
TCHAR buffer[] = _T("Hello");
TCHAR buffer1[] = _T("How to clear this one\?");
TextOut(hdc,200,170,buffer,_tcslen(buffer));
TextOut(hdc, 200, 185, buffer1, _tcslen(buffer1));
My further text output has been overwritten like this
how to clear the previous one and add this one.
I found that doing TextOut function on the same location like this
TextOut(hdc,200,170,buffer,_tcslen(buffer));
TextOut(hdc, 200, 170, buffer1, _tcslen(buffer1));
will replace the previous one but for some other reasons I cannot do this
what is the actual way of clearing the screen.
Is there anything in windows like system("cls") for console
Thank you
The question is improperly posed, as it seems that you think that the drawable surface of a window is somehow persistent - it isn't, the system is free to forget about all of its content and call your WM_PAINT handler to have it redrawn back to the state where it was. For this reason, the question feels strange: it's not that you erase some content that is already there (the window content is ephemeral), you arrange so that your paint code no longer paints what you don't want and force a repaint.
So, if you are doing your drawing in WM_PAINT like you should, you should set some kind of flag (or probably, clear the data structures that store the elements to draw) that tells your repaint code not to draw the text and do an InvalidateRect(hwnd, TRUE) to have your window background repainted and your WM_PAINT called.
My fear however is that you are not doing painting in WM_PAINT as you should, but scattered in other places using GetDC and the like (tip: in normal applications there's almost no place where you need GetDC or CreateDC); in this case, you are already doing it wrong (as you can see by minimizing and restoring your window), and you should learn how to do it properly before going on.

Different Window Form

I have been using directx for a while now and one thing that has always bothered me is that windows are squares (I guess this applies to most programs). Now as creation often happens by defining a rectangle shape and drawing that, black for example. I have been thinking of 2 approaches to this:
Define a bigger rectangle and draw parts of the background transparent.
I decided not to go for this one as I have absolutely no idea how to do this.
See what microsoft offers when it comes to window shapes.
And while they did have a lot of win32 configuration settings (no border etc) I couldn't find anything about drawing in a particular shape (like using a triangle for example).
Does anyone have experience with window shapes or drawing a background transparent? Maybe even a better option that I missed? Thanks in advance!
This can be done quite simply using the SetWindowRgn API call. What this does is define the area within which the system will allow drawing to appear.
As a simple example, lets punch a hole in one of our windows. The following can be done in the WM_CREATE handler of the window:
case WM_CREATE:
{
// Get the window rect
RECT r;
GetWindowRect(m_hwnd, &r);
MapWindowPoints(NULL, m_hwnd, reinterpret_cast<LPPOINT>(&r), 2);
// Work out the size of the window
LONG w = r.right - r.left;
LONG h = r.bottom - r.top;
// Create a rectangular region to cover the window (almost)
HRGN hRgn = CreateRectRgnIndirect(&r);
// and a smaller elliptical window
r.left += w/4;
r.right -= w/4;
r.top += h/4;
r.bottom -= h/4;
HRGN rgnCirc = CreateEllipticRgnIndirect(&r);
// Now we combine the two regions, using XOR to create a hole
int cres = CombineRgn(hRgn, rgnCirc, hRgn, RGN_XOR);
// And set the region.
SetWindowRgn(m_hwnd, hRgn, TRUE);
}
break;
Some important notes. The region that is passed to SetWindowRgn is from that point on owned by the system, so do not perform any more operations on it. Also, you would need to modify the region if the window is resized - I only put the example in WM_CREATE as... an example.
Another little caveat about the above, it doesn't perform the calculation of window size correctly... as I said, this is just an example that punches a hole in a window.
Finally, I tried it with a simple Direct-X program, and it works with that too. Hoorah!

Plotting a graph in C++ window

I want to plot a graph out of simple functions/set of coordinates in a window. I know the c++ win32 basics and I can make a simple window with buttons and other control objects. But which is the fastest and easiest library to plot a graph to my program?
I expect you are using Win32 API (not CLR).
Theory is easy, you need to obain device context withing WM_PAINT message.
You can use main window or any child window (control - static, button) inside main window.
Here are some usefull links:
http://www.codeproject.com/Articles/2078/Guide-to-WIN32-Paint-for-Intermediates
http://www.codeproject.com/Articles/66250/BeginPaint-EndPaint-or-GetDC-ReleaseDC.aspx
eg:
case WM_PAINT:
BeginPaint(hWnd, &ps);
LineTo(ps.hDC, 30,30);
EndPaint(hWnd, &ps);
return 0;
This will draw line from 0,0 to 30,30
Here is the light, easy to use library:
http://www.codeproject.com/Articles/1546/Plot-Graphic-Library

WinAPI - avoid redraw of window

I'm drawing into a WinAPI-Window by using the SetPixel()-Function.
If I scale the window or lose focus (another window is on top) I lose the whole content that I draw into the window.
I just used
RECT rc;
GetClientRect(hwnd, &rc);
RedrawWindow(hwnd, &rc, NULL, RDW_NOERASE | RDW_NOFRAME | RDW_VALIDATE);
which helped to avoid redrawing the content when I move the window but scaling and losing focus still removes the content.
Does anyone have an idea what I missed?
Draw it to a buffer/bitmap and then draw that to your window.
When a window needs to be repainted it will be sent a WM_PAINT message. At this point you must redraw all of the window, or at least all parts of it which are contained within a clipping region. Windows does some buffering and automatic painting, specifically it will repaint parts of a window which are covered by other windows then uncovered. Once the window has been resized though, or (presumably) invalidated, you're on your own.
As #daniel suggested, if painting is an intensive process and you don't want to do it every time a repaint is required, render your content into a bitmap (which in this case will be an off-screen buffer) and BitBlt (copy) it into the window as necessary.
Grab yourself a copy of Charles Petzold's book "Programming Windows" for information about how you should go about painting. If you are writing a WinAPI app but have used SetPixel I'd recommend reading the entirety of the first few chapters to get an idea of how an old-school Windows programme should be structured.
SetPixel is very slow, you cannot improve your program significantly. Create in-memory bitmap and draw it on the window. For example, you can do this using StretchDIBits function, which draws the whole memory area as bitmap to the window, instead of SetPixel.
The most important StretchDIBits parameters are:
CONST VOID *lpBits - memory array (pixels). You need to fill it in memory instead of SetPixel calls.
CONST BITMAPINFO *lpBitsInfo - BITMAPINFO structure which must describe bitmap structure. For example, if lpBits has BGRW structure (4 bytes per pixel), BITMAPINFO must describe true color bitmap.
You need to draw the content into memory and then draw it to the window when you got WM_PAINT message. There is no way to avoid using memory buffer because window device context does not save what you draw.
Create a DIB surface and draw into it instead. Then redraw the bitmap when redrawing a window.
You're trying to draw with a pre-Windows way in Windows. ;)

Drawing issues with c++

I'm sort of new to c++ and i'm trying to create a game.
I have a 2d array RECT_GRID of rectangles.
I have a 2d array GRID of unsigned short.
I fill the rectangle array during WM_CREATE
The WM_PAINT event paints rectangles for all the elements in the array. The color of the rectangle is based on the value of GRID[x][y]
I made it so when the down key is pressed, It changes the color of one of the rectangles by setting GRID[1][XMOVE] = to a different color
then it invalidates the client rectangle
Basically what happens is, it works well for a while, but eventually it just stops drawing stuff. I checked my XMOVE variable during debug, I checked by grid values and stuff and everything is fine. When I remove the for loop from the paint event and focus on 1 specific rectangle, it never fails, but if I try to redraw all of them at once, after about 20 times, it stops painting things. What could cause this? I'm new to c++ and I bet I'm not painting properly and causing an overflow or something.
If anyone could explain what's going wrong, or a proper way to do this, I'd really appreciate it. I could not find anything like this example on Google.
Thanks
EDIT:
I'm using 3 global brushes
HBRUSH A;
HBRUSH B;
HBRUSH C;
and when I modify them, I always say A = MakeBrush(NUM);
ami I using brushes properly?
My first guess, if you're a total GDI/C++ newbie, is that you are probably creating a lot of Pens and Brushes. These are constrained resources in Windows. You can only create so many of them before you start to tax your resources. So either make your Brushes and Pens and Windows, etc all at once and re-use them, or dispose of them properly when you're done. I recommend getting a copy of "the Bible" (http://www.amazon.com/Programming-Windows%C2%AE-Fifth-Microsoft/dp/157231995X/ref=sr_1_1?ie=UTF8&s=books&qid=1252788457&sr=8-1) and reading the chapters in there about drawing.
EDIT: It doesn't sound like you're modifying your brushes properly, but since I can't see the code for MakeBrush, I don't know. You're probably creating a lot of brushes behind the scenes and you don't even know it. Seriously, get a copy of Petzold's book and spend an hour or two. You'll end up with more hair on your head later! ;-)
You'll notice in all GDI examples, a 'CreatePen' or 'CreateSolidBrush' will be followed by a delete object. This is because they are limited resources in windows, and you can run out of them if you don't delete them when you're finished.
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
HBRUSH NewBrush = CreateSolidBrush(RGB(250, 25, 5));
SelectObject(hDC, NewBrush);
Rectangle(hDC, 20, 20, 250, 125);
DeleteObject(NewBrush);
EndPaint(hWnd, &Ps);
If you forget to include the 'DeleteObject' call, then you will have problems after you use up all the brushes available. It's actually fun to try =)