Windows h c графика в консоли

155 / 137 / 46

Регистрация: 15.02.2010

Сообщений: 750

1

Графика в консоли

16.12.2012, 09:09. Показов 82603. Ответов 36


Студворк — интернет-сервис помощи студентам

Существует ли возможность реализовать графику в консольных приложениях С++ с использованием стандартных библиотек. (без создния собственных библиотек, классов и прочее)?
Например, чтобы для изображения линии использовалось что-то вроде line(x1,x2,y1,y2)
P,S. среда Visual Studio или Code Blocks.



0



Programming

Эксперт

94731 / 64177 / 26122

Регистрация: 12.04.2006

Сообщений: 116,782

16.12.2012, 09:09

Ответы с готовыми решениями:

Графика в консоли
Здравствуйте, подскажите пожалуйста, можно ли в консоли с++ устроить графический интерфейс…

Графика в консоли
Помогите разобраться с графикой в консоле.
компилирую код в Dev C++
#include <iostream>
#include…

Построение графика функции в консоли
Привет всем
Задание такое — построить график функции |y| = |sin(x)| + cos(x)
Вся сложность…

Графика в консоли
Изучаю с++ написал калькулятор для консоли, хочу переписать его в графическую версию для консоли….

36

CEO SOVAZ Corp.

386 / 232 / 51

Регистрация: 17.12.2011

Сообщений: 822

Записей в блоге: 1

16.12.2012, 09:53

2

В консоли вроде нельзя. В Qt SDK есть встроенная библиотека QPainter. Там и рисуй (только не в консоли)



0



Croessmah

Неэпический

17848 / 10616 / 2049

Регистрация: 27.09.2012

Сообщений: 26,684

Записей в блоге: 1

16.12.2012, 10:27

3

Лучший ответ Сообщение было отмечено как решение

Решение

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <windows.h>
int main(){
    POINT op;
    HWND hWnd=GetConsoleWindow();
    HDC hDC=GetDC(hWnd);
    SelectObject(hDC,GetStockObject(WHITE_PEN));
 
    MoveToEx(hDC,50,50,&op);
    LineTo(hDC,100,200);
 
    ReleaseDC(hWnd,hDC);
    std::cin.get();
    return 0;
}



8



155 / 137 / 46

Регистрация: 15.02.2010

Сообщений: 750

16.12.2012, 11:11

 [ТС]

4

Спасибо, Croessmah.
Правда, сложновато будет новичкам это втолкнуть в теме «создание рисунков циклическим повторением графических примитивов». Но попробую.
Не хочется ради этой одной темки Паскаль или Бейсик задействовать.



0



Неэпический

17848 / 10616 / 2049

Регистрация: 27.09.2012

Сообщений: 26,684

Записей в блоге: 1

16.12.2012, 11:26

5

Цитата
Сообщение от LVV
Посмотреть сообщение

«создание рисунков циклическим повторением графических примитивов»

покажите им квадрат или треугольник(ковер, салфетку) Серпинского.

Их можно достаточно ясно описать без использования программирования вовсе



0



155 / 137 / 46

Регистрация: 15.02.2010

Сообщений: 750

16.12.2012, 11:32

 [ТС]

6

Цитата
Сообщение от Croessmah
Посмотреть сообщение

можно достаточно ясно описать без использования программирования вовсе

Ну, смысл как раз в программировании и заключается. Циклы + графика: интересней для изучения, чем на одних вычислениях циклы «оттачивать»…



1



Эксперт С++

3646 / 1378 / 243

Регистрация: 16.04.2009

Сообщений: 4,526

16.12.2012, 12:39

7

LVV, я бы OpenGL юзал.



3



424 / 389 / 113

Регистрация: 21.09.2012

Сообщений: 913

16.12.2012, 12:54

8

Croessmah, А можно сделать так чтобы линия не пропадала когда консоль сворачиваешь? Чтобы перерисовка была



0



Эксперт С++

3646 / 1378 / 243

Регистрация: 16.04.2009

Сообщений: 4,526

16.12.2012, 13:13

9

v.a.l.i.d, В обработчике событий добавить в WM_PAINT перерисовку.



0



424 / 389 / 113

Регистрация: 21.09.2012

Сообщений: 913

16.12.2012, 14:23

10

Цитата
Сообщение от go
Посмотреть сообщение

В обработчике событий добавить в WM_PAINT перерисовку.

А как это сделать?



0



daslex

1369 / 592 / 199

Регистрация: 02.08.2011

Сообщений: 2,882

16.12.2012, 19:08

11

Цитата
Сообщение от Croessmah
Посмотреть сообщение

C++
1
2
#include <iostream>
#include <windows.h>

Иногда нужно

C++
1
2
3
#define _WIN32_WINNT 0x0500 
#include <iostream>
#include <windows.h>

иначе может возникнуть ошибка error C3861: ‘GetConsoleWindow’: identifier not found



0



Неэпический

17848 / 10616 / 2049

Регистрация: 27.09.2012

Сообщений: 26,684

Записей в блоге: 1

16.12.2012, 20:10

12

Цитата
Сообщение от daslex
Посмотреть сообщение

Иногда нужно

Специально для Вас: MSDN. Изменение WINVER и _WIN32_WINNT
И если уж на то пошло, то _WIN32_WINNT_WIN2K, а не 0x0500
А лучше пишите тогда под DOS



1



1369 / 592 / 199

Регистрация: 02.08.2011

Сообщений: 2,882

16.12.2012, 20:42

13

Цитата
Сообщение от Croessmah
Посмотреть сообщение

Специально для Вас

Не по теме:

ну не только для меня. будут люди, которые с этим столкнутся еще.



0



skynet120

0 / 0 / 0

Регистрация: 22.01.2013

Сообщений: 76

01.09.2013, 15:14

14

Цитата
Сообщение от Croessmah
Посмотреть сообщение

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <windows.h>
int main(){
    POINT op;
    HWND hWnd=GetConsoleWindow();
    HDC hDC=GetDC(hWnd);
    SelectObject(hDC,GetStockObject(WHITE_PEN));
 
    MoveToEx(hDC,50,50,&op);
    LineTo(hDC,100,200);
 
    ReleaseDC(hWnd,hDC);
    std::cin.get();
    return 0;
}

кто может рассказать как оно работает, буду очень благодарен



0



Croessmah

Неэпический

17848 / 10616 / 2049

Регистрация: 27.09.2012

Сообщений: 26,684

Записей в блоге: 1

01.09.2013, 15:18

15

Если просто

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <windows.h>
int main(){
    POINT op;
    HWND hWnd=GetConsoleWindow(); //Получаем дескриптор окна консоли
    HDC hDC=GetDC(hWnd); //Получаем контекст устройства по полученному дескриптору
    SelectObject(hDC,GetStockObject(WHITE_PEN)); //Выбираем перо WHITE_PEN в контекст
 
    MoveToEx(hDC,50,50,&op); //Ставим текущую точку в координаты 50,50
    LineTo(hDC,100,200); //Рисуем линию из текущей точки в точку 100, 200
 
    ReleaseDC(hWnd,hDC); //"Освобождаем" контекст
    std::cin.get();
    return 0;
}



2



0 / 0 / 0

Регистрация: 22.01.2013

Сообщений: 76

01.09.2013, 15:27

16

можно ли где почитать про это все

интересует:
1) какие параметры за что отвечают
2) можно ли менять толщину линии



0



Неэпический

17848 / 10616 / 2049

Регистрация: 27.09.2012

Сообщений: 26,684

Записей в блоге: 1

01.09.2013, 15:31

17

Цитата
Сообщение от skynet120
Посмотреть сообщение

можно ли где почитать про это все

MSDN, книги и статьи по WinAPI

Добавлено через 1 минуту

Цитата
Сообщение от skynet120
Посмотреть сообщение

какие параметры за что отвечают

Смотря параметры чего.

Цитата
Сообщение от skynet120
Посмотреть сообщение

как нарисовать горизонтальную линию

координаты поменять

Цитата
Сообщение от skynet120
Посмотреть сообщение

можно ли менять толщину линии

создать перо с нужной толщиной



1



Модератор

Эксперт по электронике

8852 / 6631 / 903

Регистрация: 14.02.2011

Сообщений: 23,335

01.09.2013, 15:32

18

Цитата
Сообщение от LVV
Посмотреть сообщение

Существует ли возможность реализовать графику в консольных приложениях С++

в общих случаях нельзя
консоль ведь не только под Виндос бывает
а для Винды тебе показал Croessmah,
но это немножко обманка
там консоль воспринимается не как консоль а как окно консоли
о чем говорят строчки

Цитата
Сообщение от Croessmah
Посмотреть сообщение

#include <windows.h>
……..
* * HWND hWnd=GetConsoleWindow(); //Получаем дескриптор окна консоли
* * HDC hDC=GetDC(hWnd); //Получаем контекст устройства по полученному дескриптору

и если развернуть на весь экран ctrl+Enter то не во всех виндах работать будет



0



0 / 0 / 0

Регистрация: 22.01.2013

Сообщений: 76

01.09.2013, 16:11

19

Цитата
Сообщение от Croessmah
Посмотреть сообщение

создать перо с нужной толщиной

можете подсказать как это сделать

Цитата
Сообщение от Croessmah
Посмотреть сообщение

MSDN, книги и статьи по WinAPI

можете кинуть ссылку на страницу MSDN где описываться тот код что Вы привели



0



567 / 198 / 70

Регистрация: 25.05.2012

Сообщений: 816

01.09.2013, 18:27

20

Цитата
Сообщение от go
Посмотреть сообщение

v.a.l.i.d, В обработчике событий добавить в WM_PAINT перерисовку.

Для консоли этого сделать не получится.



0



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

01.09.2013, 18:27

20

What is the best way to draw things in the Console Window on the Win 32 platform using C++?

I know that you can draw simple art using symbols but is there a way of doing something more complex like circles or even bitmaps?

asked Dec 20, 2009 at 21:41

0

Yes, it is possible.

Get the HWND of the console window using GetConsoleWindow and then draw in it.

#define _WIN32_WINNT 0x601
#include <windows.h>
#include <stdio.h>

int main() {
    // Get window handle to console, and device context
    HWND console_handle = GetConsoleWindow();
    HDC device_context = GetDC(console_handle);

    //Here's a 5 pixels wide RED line [from initial 0,0] to 300,300
    HPEN pen = CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
    SelectObject(device_context, pen);
    LineTo(device_context, 300, 300);

    ReleaseDC(console_handle, device_context);

    getchar();

    return 0;
}

Note: GetConsoleWindow was introduced in Windows 2000. It’s available when _WIN32_WINNT is set to 0x500 or greater.

rustyx's user avatar

rustyx

81k26 gold badges200 silver badges268 bronze badges

answered Nov 4, 2012 at 18:59

Tapani's user avatar

TapaniTapani

1811 silver badge2 bronze badges

0

No you can’t just do that because Win32 console doesn’t support those methods. You can however use GDI to draw on the console window.

This is a great example of drawing a bitmap on a console by creating a child window on it:
http://www.daniweb.com/code/snippet216431.html

And this tells you how to draw lines and circles:
http://www.daniweb.com/code/snippet216430.html

This isn’t really drawing in the console though. This is sort of drawing «over» the console but it still does the trick pretty well.

answered Dec 20, 2009 at 21:46

Kristina's user avatar

KristinaKristina

15.9k29 gold badges111 silver badges182 bronze badges

It is possible, albeit totally undocumented, to create a console screen buffer that uses an HBITMAP that is shared between the console window process and the calling process. This is the approach that NTVDM takes to display graphics once a DOS application switches to graphics mode.

See it.

MPelletier's user avatar

MPelletier

16.3k15 gold badges86 silver badges137 bronze badges

answered Jan 12, 2010 at 21:36

Koro's user avatar

KoroKoro

6873 silver badges4 bronze badges

As Nick Brooks has pointed out, you can use GDI calls in console apps, but the graphics cannot appear in the same window as the text console I/O. This may not matter since you can draw text elements in GDI.

A simplified interface to GDI calls in console apps is provided by WinBGIm. It is a clone of Borland’s DOS BGI API, but with extensions to handle resizable windows, mouse input, and 24bit colour models. Since it is available as source code, it also serves a good demonstration of using GDI in this way.

It is possible to either have both a console and the GDI window, or you can suppress the console window by specifying that the application is a GUI app (the -mwindows linker option in GNU toolchain) — note that specifying a GUI app really only suppresses the console, it is only really a GUI app if it has a message loop. Having the console is good for debugging, since it is where stdout and stderr are output to by default.

answered Dec 20, 2009 at 22:11

Clifford's user avatar

CliffordClifford

88.8k13 gold badges86 silver badges165 bronze badges

Not without usng ASCII art. Back in the days of DOS it was «fairly» easy to do by redesigning the character bitmaps. It might only be possible in windows by creating your own font, but im really not sure thats possible

answered Dec 20, 2009 at 21:45

Goz's user avatar

GozGoz

61.4k24 gold badges124 silver badges204 bronze badges

6

То, что вы рисуете напрямую в окне консоли, затирается стандартным обработчиком отрисовки conhost при следующем обновлении окна (как при выводе нового текста в консоль, так и при изменении, например, размеров ее окна). Переопределить этот обработчик нельзя, но можно отобразить свое окно поверх консоли, как показано в примере ниже. Кроме того, код рисования графика в вопросе рисует график по отдельным точкам, я переделал его с SetPixel на MoveTo/LineTo, чтобы он выглядел непрерывным.

#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNINGS
#include <math.h>
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <string>
#include <clocale>
#include <iostream>

WCHAR szTitle[] = L"Conhoid";
WCHAR szWindowClass[] = L"ConhoidWndClass";

//координаты окна
const int WND_X = 300;
const int WND_Y = 50;
const int WND_W = 400;
const int WND_H = 400;

ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI Draw(HDC hdc);

DWORD WINAPI Run(LPVOID lpThreadParameter)
{   
    HINSTANCE hInstance = GetModuleHandle(NULL);
    HWND hCon = GetConsoleWindow();
    RECT rc;
    MSG msg;

    MyRegisterClass(hInstance); //инициализация класса окна 
    GetWindowRect(hCon, &rc); //получаем положение окна консоли     

    //создаем окно
    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_BORDER, rc.left + WND_X, rc.top + WND_Y, WND_W, WND_H, hCon,
        nullptr, hInstance, nullptr); 

    SetWindowLong(hWnd, GWL_STYLE, 0); //убираем строку заголовка

    if (hWnd == nullptr)
    {
        printf("Error CreateWindown");
        return 0;
    }

    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd); 

    //запуск цикла обработки сообщений
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);

    return RegisterClassExW(&wcex);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    switch (message)
    {   
    case WM_PAINT:

        PAINTSTRUCT ps;
        hdc = BeginPaint(hWnd, &ps);
        Draw(hdc);
        EndPaint(hWnd, &ps);

    break;  
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

void Conhoid(void) {
    float h = M_PI / 180, x, y, a = 20, r = 40;
    float f = -3;
    while (f <= 3) {
        x = a + r * cos(f);
        y = a * sin(f) / cos(f) + r * sin(f);
        printf("x = %f     y = %fn", x, y);
        f += h;
    }
}

DWORD WINAPI Stream(LPVOID lparoun) {
    Conhoid();
    return 0;
}

DWORD WINAPI Draw(HDC hdc) {        

    float h = M_PI / 180, x, y, a = 20, r = 40;
    float x0, y0;
    float f = -3;

    x = a + r * cos(f);
    y = a * sin(f) / cos(f) + r * sin(f);
    x0 = x;
    y0 = y;
    SelectObject(hdc, GetStockObject(WHITE_PEN));
    MoveToEx(hdc, 300 + x, 50 + y, nullptr);

    while (f <= 3) {
        x = a + r * cos(f);
        y = a * sin(f) / cos(f) + r * sin(f);

        if (abs(y - y0) < 1000) {
            LineTo(hdc, 300 + x, 50 + y);
        }
        else {
            MoveToEx(hdc, 300 + x, 50 + y, nullptr);
        }

        f += h;
        x0 = x; y0 = y;
    }

    return 0;
}

int main()
{
    setlocale(LC_ALL, "RUSSIAN");
    DWORD dwStream = 1;
    HANDLE hStream, drawStream;
    hStream = CreateThread(NULL, 0, Stream, &dwStream, 0, &dwStream);
    drawStream = CreateThread(NULL, 0, Run, &dwStream, 0, &dwStream);
    if (hStream == NULL)
        printf("Поток не запущенn");
    else {
        printf("Поток завершилсяn");
        printf("n");
        CloseHandle(hStream);
    }
    _getch();
    return 0;
}

console graph

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <windows.h>
#include <GL/glut.h>
#include <cmath>
using namespace std;
 
//const double PI = 3.141592653589793238463;
//const float  PI_F = 3.14159265358979f;
 
// Window size
int width = 512;
int height = 512;
 
// Points
const int nPoinst = 20;
const int halfOfNPoints = nPoinst / 2;
float points[nPoinst] = {
    -500, 100,
    -400, -100,
    -300, 100,
    -200, -100,
    -100, 100,
    0, -100,
    100, 100,
    200, -100,
    300, 100,
    400, -100
};
 
void DrawAxis();
void DrawStrip();
void Enable2D(int width, int height);
 
void Init()
{
    Enable2D(width, height);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
 
void Draw()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
 
    DrawAxis();
    DrawStrip();
 
    glutSwapBuffers();
}
 
void DrawStrip()
{
    glBegin(GL_LINE_STRIP);
    glColor3f(0.0f, 0.0f, 0.0f);
    for (int i = 0, index = 0; i < halfOfNPoints; ++i, index += 2)
    {
        glVertex2f(points[index], points[index + 1]);
    }
    glEnd();
}
 
void DrawAxis()
{
    // Draw X axis
    glBegin(GL_LINES);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex2f(-width / 2.0f, 0.0f);
    glVertex2f(width / 2.0f, 0.0f);
    glEnd();
 
    // Draw Y axis
    glBegin(GL_LINES);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex2f(0.0f, -height / 2.0f);
    glVertex2f(0.0f, height / 2.0f);
    glEnd();
}
 
void Enable2D(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-width / 2.0f, width / 2.0f, -height / 2.0f, height / 2.0f, 0.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
 
 
int main(int argc, char** argv)
{
    // Initialize opengl (via glut)
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(width, height);
    glutCreateWindow("A");
    glutDisplayFunc(Draw);
    Init();
    glutMainLoop();
    return 0;
}
 

Задача Для x∈[0;7] построить график функции y=sin(x).

 
График функции y=sin(x) симметричен относительно горизонтальной оси, поэтому должен быть построен в окне со смещением относительно левого верхнего угла окна, принятого за начало отсчета.

Смещение осей
Координата X меняется в пределах [0;7], координата Y[-1;1]. Величины MAX_X и MAX_Y представляют собой область допустимых значений по осям координат:

  • MAX_X = maxX — minX = 7;
  • MAX_Y = maxY — minY = 2.

Смещение графика функции представляет собой положение первой точки относительно начала отсчета левого верхнего угла:

  • смещение по оси X: OffsetX = minX*width/MAX_X = 0;
  • смещение по оси Y: OffsetY = maxY *height/MAX_Y

Значение minX=0 представляет собой минимальное значение координаты x из области допустимых значений.

Значение maxY=1 представляет собой максимальное значение координаты y из области допустимых значений. Рассматривается именно максимальное значение, поскольку за начало отсчета принят левый верхний угол окна, и координата y увеличивается по направлению вниз.

Для того чтобы график функции разместился в окне необходимо рассчитать масштабные коэффициенты по осям. Масштабный коэффициент представляет собой отношение размера окна к области допустимых значений функции.

  • масштабный коэффициент X: ScaleX = width / MAX_X;
  • масштабный коэффициент Y: ScaleY = height / MAX_Y.

Вычисление координат следующей точки (x;y) графика в окне будет осуществляться по формулам:

  • координата X=OffsetX + x*ScaleX;
  • координата Y=OffsetY + y*ScaleY,

где (x;y) — координаты точки, полученные из функции y=sin(x).

 
Реализация на C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

#include <windows.h>
#include <math.h>
const int NUM = 70; // количество точек
LONG WINAPI WndProc(HWND, UINT, WPARAM, LPARAM);
double **x; // массив данных
      // Задание исходных данных для графика
      // (двумерный массив, может содержать несколько рядов данных)
double ** getData(int n)
{
  double **f;
  f = new double*[2];
  f[0] = new double[n];
  f[1] = new double[n];
  for (int i = 0; i<n; i++)
  {
    double x = (double)i * 0.1;
    f[0][i] = x;
    f[1][i] = sin(x);
  }
  return f;
}
// Функция рисования графика
void DrawGraph(HDC hdc, RECT rectClient,
  double **x, // массив данных
  int n, // количество точек
  int numrow = 1) // количество рядов данных (по умолчанию 1)
{
  double OffsetY, OffsetX;
  double MAX_X, MAX_Y;
  double ScaleX, ScaleY;
  double min, max;
  int height, width;
  int X, Y; // координаты в окне (в px)
  HPEN hpen;
  height = rectClient.bottom — rectClient.top;
  width = rectClient.right — rectClient.left;
  // Область допустимых значений X
  min = x[0][0];
  max = x[0][0];
  for (int i = 0; i<n; i++)
  {
    if (x[0][i] < min) min = x[0][i];
    if (x[0][i] > max) max = x[0][i];
  }
  double temp = max — min;
  MAX_X = max — min;
  OffsetX = min*width / MAX_X; // смещение X
  ScaleX = (double)width / MAX_X; // масштабный коэффициент X
                  // Область допустимых значений Y
  min = x[1][0];
  max = x[1][0];
  for (int i = 0; i<n; i++)
  {
    for (int j = 1; j <= numrow; j++)
    {
      if (x[j][i] < min) min = x[j][i];
      if (x[j][i] > max) max = x[j][i];
    }
  }
  MAX_Y = max — min;
  OffsetY = max*height / (MAX_Y); // смещение Y
  ScaleY = (double)height / MAX_Y; // масштабный коэффициент Y
                   // Отрисовка осей координат
  hpen = CreatePen(PS_SOLID, 0, 0); // черное перо 1px
  SelectObject(hdc, hpen);
  MoveToEx(hdc, 0, OffsetY, 0); // перемещение в точку (0;OffsetY)
  LineTo(hdc, width, OffsetY); // рисование горизонтальной оси
  MoveToEx(hdc, OffsetX, 0, 0); // перемещение в точку (OffsetX;0)
  LineTo(hdc, OffsetX, height); // рисование вертикальной оси (не видна)
  DeleteObject(hpen); // удаление черного пера
             // Отрисовка графика функции
  int color = 0xFF; // красное перо для первого ряда данных
  for (int j = 1; j <= numrow; j++)
  {
    hpen = CreatePen(PS_SOLID, 2, color); // формирование пера 2px
    SelectObject(hdc, hpen);
    X = (int)(OffsetX + x[0][0] * ScaleX); // начальная точка графика
    Y = (int)(OffsetY — x[j][0] * ScaleY);
    MoveToEx(hdc, X, Y, 0); // перемещение в начальную точку
    for (int i = 0; i<n; i++)
    {
      X = OffsetX + x[0][i] * ScaleX;
      Y = OffsetY — x[j][i] * ScaleY;
      LineTo(hdc, X, Y);
    }
    color = color << 8; // изменение цвета пера для следующего ряда
    DeleteObject(hpen); // удаление текущего пера
  }
}
// Главная функция
int WINAPI WinMain(HINSTANCE hInstance,
  HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG msg;
  WNDCLASS w;
  x = getData(NUM); // задание исходны данных
  memset(&w, 0, sizeof(WNDCLASS));
  w.style = CS_HREDRAW | CS_VREDRAW;
  w.lpfnWndProc = WndProc;
  w.hInstance = hInstance;
  w.hbrBackground = CreateSolidBrush(0x00FFFFFF);
  w.lpszClassName = «My Class»;
  RegisterClass(&w);
  hwnd = CreateWindow(«My Class», «График функции»,
    WS_OVERLAPPEDWINDOW,
    500, 300, 500, 380, NULL, NULL,
    hInstance, NULL);
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);
  while (GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}
// Оконная функция
LONG WINAPI WndProc(HWND hwnd, UINT Message,
  WPARAM wparam, LPARAM lparam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  switch (Message)
  {
  case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps);
    DrawGraph(hdc, ps.rcPaint, x, NUM); // построение графика
                      // Вывод текста y=sin(x)
    SetTextColor(hdc, 0x00FF0000); // синий цвет букв
    TextOut(hdc, 10, 20, «y=sin(x)», 8);
    EndPaint(hwnd, &ps);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hwnd, Message, wparam, lparam);
  }
  return 0;
}

 
Результат выполнения
График функции sin(x)
Примечание: Для корректной сборки приложения используется многобайтовая кодировка.

Назад: Задачи и их решения

I have drawn the straight line using windows.h in code::blocks. I can’t explain it in details, but I can provide you a code and procedure to compile it in code::blocks.

  1. go to setting menu and select compiler and debugger.
  2. Click on linker tab and add a link library libgdi32.a which is at C:Program FilesCodeBlocksMinGWlib directory.

Now compile this program

#include <windows.h>

#include <cmath>

#define ROUND(a) ((int) (a + 0.5))

/* set window handle */

static HWND sHwnd;

static COLORREF redColor=RGB(255,0,0);

static COLORREF blueColor=RGB(0,0,255);

static COLORREF greenColor=RGB(0,255,0);


void SetWindowHandle(HWND hwnd){

sHwnd=hwnd;

}

/* SetPixel */

void setPixel(int x,int y,COLORREF& color=redColor){

if(sHwnd==NULL){

    MessageBox(NULL,"sHwnd was not initialized !","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

HDC hdc=GetDC(sHwnd);

SetPixel(hdc,x,y,color);

ReleaseDC(sHwnd,hdc);

return;

// NEVERREACH //

}


void drawLineDDA(int xa, int ya, int xb, int yb){

   int dx = xb - xa, dy = yb - ya, steps, k;

   float xIncrement, yIncrement, x = xa, y = ya;

   if(abs(dx) > abs(dy)) steps = abs(dx);

   else steps = abs(dy);

   xIncrement = dx / (float) steps;

   yIncrement = dy / (float) steps;

   setPixel(ROUND(x), ROUND(y));

   for(int k = 0; k < steps; k++){

    x += xIncrement;

    y += yIncrement;

    setPixel(x, y);

 }

}

/* Window Procedure WndProc */

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

 switch(message){

    case WM_PAINT:

        SetWindowHandle(hwnd);

        drawLineDDA(10, 20, 250, 300);

        break;

    case WM_CLOSE: // FAIL THROUGH to call DefWindowProc

        break;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    default:

    break; // FAIL to call DefWindowProc //

  }

 return DefWindowProc(hwnd,message,wParam,lParam);

}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int      iCmdShow){

static TCHAR szAppName[] = TEXT("Straight Line");

WNDCLASS wndclass;

wndclass.style         = CS_HREDRAW|CS_VREDRAW ;

wndclass.lpfnWndProc   = WndProc ;

wndclass.cbClsExtra    = 0 ;

wndclass.cbWndExtra    = 0 ;

wndclass.hInstance     = hInstance ;

wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName  = NULL ;

wndclass.lpszClassName = szAppName ;

// Register the window //

if(!RegisterClass(&wndclass)){

    MessageBox(NULL,"Registering the class failled","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

// CreateWindow //

HWND hwnd=CreateWindow(szAppName,"DDA - Programming Techniques",

            WS_OVERLAPPEDWINDOW,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             NULL,

             NULL,

             hInstance,

             NULL);

if(!hwnd){

    MessageBox(NULL,"Window Creation Failed!","Error",MB_OK);

    exit(0);

  }

  // ShowWindow and UpdateWindow //

  ShowWindow(hwnd,iCmdShow);

 UpdateWindow(hwnd);

 // Message Loop //

 MSG msg;

 while(GetMessage(&msg,NULL,0,0)){

    TranslateMessage(&msg);

    DispatchMessage(&msg);

 }

  /* return no error to the operating system */

  return 0;

}

In this program I have used DDA line drawing algorithm. Pixel drawing tasks is done by setPixel(ROUND(x), ROUND(y)) function.
This is windows programing which you can learn details here

I have drawn the straight line using windows.h in code::blocks. I can’t explain it in details, but I can provide you a code and procedure to compile it in code::blocks.

  1. go to setting menu and select compiler and debugger.
  2. Click on linker tab and add a link library libgdi32.a which is at C:Program FilesCodeBlocksMinGWlib directory.

Now compile this program

#include <windows.h>

#include <cmath>

#define ROUND(a) ((int) (a + 0.5))

/* set window handle */

static HWND sHwnd;

static COLORREF redColor=RGB(255,0,0);

static COLORREF blueColor=RGB(0,0,255);

static COLORREF greenColor=RGB(0,255,0);


void SetWindowHandle(HWND hwnd){

sHwnd=hwnd;

}

/* SetPixel */

void setPixel(int x,int y,COLORREF& color=redColor){

if(sHwnd==NULL){

    MessageBox(NULL,"sHwnd was not initialized !","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

HDC hdc=GetDC(sHwnd);

SetPixel(hdc,x,y,color);

ReleaseDC(sHwnd,hdc);

return;

// NEVERREACH //

}


void drawLineDDA(int xa, int ya, int xb, int yb){

   int dx = xb - xa, dy = yb - ya, steps, k;

   float xIncrement, yIncrement, x = xa, y = ya;

   if(abs(dx) > abs(dy)) steps = abs(dx);

   else steps = abs(dy);

   xIncrement = dx / (float) steps;

   yIncrement = dy / (float) steps;

   setPixel(ROUND(x), ROUND(y));

   for(int k = 0; k < steps; k++){

    x += xIncrement;

    y += yIncrement;

    setPixel(x, y);

 }

}

/* Window Procedure WndProc */

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

 switch(message){

    case WM_PAINT:

        SetWindowHandle(hwnd);

        drawLineDDA(10, 20, 250, 300);

        break;

    case WM_CLOSE: // FAIL THROUGH to call DefWindowProc

        break;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    default:

    break; // FAIL to call DefWindowProc //

  }

 return DefWindowProc(hwnd,message,wParam,lParam);

}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int      iCmdShow){

static TCHAR szAppName[] = TEXT("Straight Line");

WNDCLASS wndclass;

wndclass.style         = CS_HREDRAW|CS_VREDRAW ;

wndclass.lpfnWndProc   = WndProc ;

wndclass.cbClsExtra    = 0 ;

wndclass.cbWndExtra    = 0 ;

wndclass.hInstance     = hInstance ;

wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName  = NULL ;

wndclass.lpszClassName = szAppName ;

// Register the window //

if(!RegisterClass(&wndclass)){

    MessageBox(NULL,"Registering the class failled","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

// CreateWindow //

HWND hwnd=CreateWindow(szAppName,"DDA - Programming Techniques",

            WS_OVERLAPPEDWINDOW,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             NULL,

             NULL,

             hInstance,

             NULL);

if(!hwnd){

    MessageBox(NULL,"Window Creation Failed!","Error",MB_OK);

    exit(0);

  }

  // ShowWindow and UpdateWindow //

  ShowWindow(hwnd,iCmdShow);

 UpdateWindow(hwnd);

 // Message Loop //

 MSG msg;

 while(GetMessage(&msg,NULL,0,0)){

    TranslateMessage(&msg);

    DispatchMessage(&msg);

 }

  /* return no error to the operating system */

  return 0;

}

In this program I have used DDA line drawing algorithm. Pixel drawing tasks is done by setPixel(ROUND(x), ROUND(y)) function.
This is windows programing which you can learn details here

case MNUCLEAR:

Clear();

curspos = { 0, 1 };

// после очистки курсор

//в левый верхний угол консоли

break;

case MNUEXIT: int resp;

cout << «Вы уверены, что хотите выйти

из программы? (y/n)?»;

resp = getchar();

if (resp == ‘y’ || resp == ‘Y’)

{ gotoxy(0, 0); cls(1); exit(0); } getCursorPosition(); // запомнить положение курсора,

// если отменили выход

break;

}

fflush(stdin); //очистить буфер клавиатуры gotoxy(menu[sel].x, menu[sel].y); // курсор в

// текущий пункт меню

showCursor(false);

break;

case 120: // выход по клавише x case 88: // выход по клавише X case 27: // выход по клавише ESC

gotoxy(0, 0); cls(1);

exit(0); //завершение программы

}

}

}

}

//Текстовый курсор в точку x,y void gotoxy(int x, int y)

{

COORD cursorPos = { x, y }; SetConsoleCursorPosition(hStdOut, cursorPos); //SetConsoleCursorPosition(hStdOut, {x,y});

}

//запись текущего положения текстового курсора в глобальную

//переменную curspos

void getCursorPosition(void)

{

GetConsoleScreenBufferInfo(hStdOut, &csbInfo);

curspos = csbInfo.dwCursorPosition;// положение курсора

}

//очистка тестовой области консоли. Если it==0, то очистка со

//строки следующей за строкой меню, иначе очистка с левого

//верхнего угла консоли

31

void cls(int it)

{

int i;

string s(80, ‘ ‘);

SetConsoleTextAttribute(hStdOut, woкkWindowAttributes); if (it == 0) gotoxy(0, consolRect.Top + 1);

else gotoxy(0, consolRect.Top);

for (i = consolRect.Top; i<curspos.Y+1; i++) // очистка от

// первой строки до строки с курсором cout << s.c_str(); // залить фон строки меню

gotoxy(0, 0);

}

//выделить пункт меню с номером sel void itemMenu(int sel, bool activate)

{

WORD itemAttributes;

if (activate) itemAttributes = activeItemAttributes; else itemAttributes = inactiveItemAttributes; gotoxy(menu[sel].x, menu[sel].y); SetConsoleTextAttribute(hStdOut, itemAttributes); cout << menu[sel].str;

}

//скрыть/показать текстовый курсор в консоли

void showCursor(bool visible)

{

CONSOLE_CURSOR_INFO ccInfo; ccInfo.bVisible = visible; ccInfo.dwSize = 20; SetConsoleCursorInfo(hStdOut, &ccInfo);

}

Большую часть кода этого модуля составляет определение функции DrawMenu(). В ней «спрятана» вся функциональность меню. Функция рисует/печатает меню в верхней строке консольного окна, а затем запускает бесконечный цикл обработки событий нажатий клавиш. Комментарии в коде поясняют основные команды. Затем в модуле определяются некоторые вспомогательные функции.

Функция gotoxy(int x,int y) устанавливает курсор в заданную позицию.

Функция getCursorPosition(void) сохраняет текущее положение курсора в глобальную переменную curspos.

Функция cls(int it) выполняют очистку консоли (всей или без строки меню).

Функция itemMenu(int sel,bool activate) выделяет или снимает выделение пункта меню.

Функция showCursor(bool visible) делает невидимым или показывает текстовый курсор.

32

Замечание. Нашему меню далеко до функциональности, которую вы наблюдаете в меню программ Windows. Однако оно дает представление о том, как можно создать меню в текстовом режиме консоли. Вы можете использовать нашу программу как шаблон для ваших консольных программ. Все, что вам надо изменить находится в цикле обработки событий нажатий клавиш функции DrawMenu(), а также небольшом фрагменте кода, который еще раз приведен ниже.

// Изменяемые элементы меню

enum menuitems { MNUFILE, MNUDO, MNUCLEAR, MNUEXIT }; extern const int numMenu = 4; //количество пунктов меню

ITEM menu[numMenu] = {

//положение (x,y), заголовок,

// указатель на функцию

{ 1, 0, » Файл

«, File },

{11, 0, » Действие «, Do },

{21, 0, » Очистить «, Clear },

{ 31, 0, » Выход

«, Exit }

};

Изменения в программе, которые должен выполнить пользователь состоят в следующем:

изменить количество пунктов меню, задав значение константы numMenu;

Определить функции, которые будут вызываться при выборах пунктов меню (у нас функции File, Do, Clear, Exit определены в модуле menudemo.cpp) ;

добавить описание функций меню в модуль menudemo.h;

изменить количество пунктов и их заголовки в массиве menu[]; при

этом длины строк заголовков (в нашем примере » Файл «, » Действие «, » Очистить «, » Выход «) должна быть подобрана в соответствии с их первой координатой в этом массиве; у нас длина строк заголовков меню равна 10 символам);

изменить/добавить имена констант в перечислении menuitems; элементов в перечислении должно быть столько, сколько элементов меню;

в функции DrawMenu() в цикл обработки нажатий клавиш в раздел case KEY_ENTER внутри тела оператора switch(sel) нужно добавить код обработки выбора соответствующих пунктов меню. Например, если вы добавите в перечисление menuitems константу MYMENU, а функция, которая будет обрабатывать соответствующий ей пункт меню, будет называться mymenu(), то вы должны будете добавить

следующий код:

case MYMENU: mymenu();

getCursorPosition();

break;

33

У нашего строчного меню есть недостаток – оно привязано к первой текстовой строке консоли. При прокрутке текста в консоли оно будет также прокручиваться. Вы можете попробовать самостоятельно доработать программу, чтобы это неудобство устранить.

Сделаем еще одно замечание. Большинство функций, которые определяют дескриптор окна, определяют или устанавливают его размер, атрибуты и т.д. могут не справиться со своей задачей. В этом случае они возвращают соответствующие значения, которые нужно (и правильно) проверять. Для простоты контроль ошибок в наших примерах не был реализован. Однако в реально функционирующем приложении такой контроль обязателен.

Например, чтение дескриптора консольного окна может выполняться следующим образом

hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout == INVALID_HANDLE_VALUE) {

MessageBox(NULL, L»Ошибка получения дескриптора», L»Ошибка», MB_OK | MB_ICONERROR);

return 1;

}

Здесь функция MessageBox выводит простое окно сообщений

Первый ее аргумент является идентификатором родительского окна или NULL, если такового нет. Второй аргумент является указателем на двухбайтовую строку, которая выводится в теле окна сообщения. Обычная строка превращается в массив символов типа wchar_t (каждый символ занимает в памяти два байта), если перед строкой указать префикс L. Окно MessageBox является диалоговым окном Windows и, поэтому, использует двухбайтовые коды символов в стандарте Unicode, в отличии от однобайтовых строк консоли. Третий аргумент является указателем на двухбайтовую строку, которая выводится в заголовке окна. Последний параметр типа UINT определяет какие в окне сообщений отображаются кнопки и иконки. Установка битов этого беззнакового целого числа говорит о том будет ли включен тот или иной элемент интерфейса окна сообщений. Чаще всего его задают комбинацией флагов. В приведенном выше фрагменте кода мы задали этот аргумент в виде комбинации двух флагов MB_OK | MB_ICONERROR. Первый из них говорит о необходимости отобразить кнопку OK, второй выводит иконку . А

например, флаг MB_YESNOCANCEL выводит в окне сообщений три кнопки:

.

Функция MessageBox возвращает целое значение, которое зависит от того, какая кнопка была нажата в окне сообщений.

34

Существует много других, не затронутых нами функций, которые предназначены для управления консольным окном. Познакомиться с ними вы можете по справочной системе в разделе «Console Functions».

1.2 Рисование в консольном окне

В этом параграфе мы покажем, как можно применять графические функции Windows для рисования в консольном окне. Однако помните, что графические возможности этого окна недостаточно функциональны и основным режимом работы консоли является текстовый. Тем не менее, все, что здесь будет изложено, без всяких изменений, но с некоторыми существенными улучшениями функциональности, используется в «оконных» приложениях

Windows.

Все графические функции C/C++ в Windows используют специальную структуру, которая называется контекстом окна. Мы здесь не будем описывать ее подробно. Вы должны знать, что она хранит текущие параметры области, в которой мы рисуем: цвет линий, их толщину и стиль (пунктир, сплошной,…), цвет заливки областей, размеры окна и т.д. В программах на C/C++ контекст окна хранится в переменной типа HDC, которая должна быть связана с окном, в котором происходит рисование. Простейшая команда, которая создает такую переменную, имеет вид

HDC hdc = GetDC(GetConsoleWindow());

Сейчас вы можете не задумываться о ее смысле – просто включайте эту строку в начало функции main(), а возвращенный ее дескриптор используйте во всех графических функциях. Любая функция WINDOWS, которая что-то рисует, первым параметром будет принимать переменную hdc. Перед завершением программы дескриптор контекста консольного окна рекомендуется освободить командой

ReleaseDC(NULL, hdc);

Первый параметр представляет идентификатор окна HWND, который может использоваться разными функциями программы. Обычно его получают командой

HWND hwnd = GetConsoleWindow();

Если он использовался в программе, то его идентификатор hwnd должен был бы быть передан первым аргументом в функцию ReleaseDC(hwnd,hdc).

В случае ошибки функции GetConsoleWindow() и GetDC() возвращают специальные значения, которые рекомендуется проверять, например, следующим образом:

HWND hwnd = GetConsoleWindow(); HDC hdc;

if (hwnd!=NULL)

{

hdc=GetWindowDC(hwnd);

if(hdc==0) cout<<«Error DC Window»<<endl;

}

35

else

cout << «Error Find Window» << endl;

Для упрощения кода в примерах этого параграфа мы не будем выполнять множество проверок значений, возвращаемых функциями, которые получают или создают различные объекты Windows (или их идентификаторы). Однако в реально функционирующих приложениях такой контроль обязателен.

При работе с графикой используется система координат при которой по умолчанию левый верхний угол окна имеет координаты (0,0), оси направлены вправо и вниз, а логическими единицами являются пиксели.

Пример 1a. В этом примере мы рисуем прямоугольник в консольном окне.

#include <windows.h>

void main()

{

HDC hdc = GetDC(GetConsoleWindow()); Rectangle(hdc, 100, 60, 180, 160); system(«pause»);

}

Здесь переменная hdc, содержащая дескриптор контекста консольного окна, передается первым аргументом функции Rectangle(hdc,…), которая рисует прямоугольник. Другие аргументы этой функции – координаты левого верхнего и правого нижнего углов xleft , yleft , xright , yright .

Начало координат находится в левом верхнем углу рабочей области окна. Горизонтальная ось X направлена вправо, вертикальная Y – вниз. По умолчанию единицами измерения являются пиксели.

Обратите внимание на поведение рисунка после сворачивания консольного окна, перекрытия его другим окном, изменении размера или прокручивании его внутренней области. Графика исчезает! Этого недостатка обычно нет у стандартных окон Windows потому, что они «умеют» автоматически перерисовывать свою рабочую область после событий сворачивания, перекрытия или изменения размера. В учебных консольных программах перерисовку можно выполнять вручную. Для этого любое рисование следует помещать внутрь создаваемой вами функции draw(.), и вызывать ее повторно в случае, если графика окна исчезла.

Пример 1b. Более гибкая версия консольной программы с графикой.

#include <windows.h> #include <iostream> #include <conio.h> using namespace std;

void draw(HWND hwnd, HDC hdc); void setConsoleSize();

36

void main()

{

SetConsoleTitle(L»Simple Rectangle Drawing»); HWND hwnd = GetConsoleWindow();

HDC hdc = GetDC(hwnd);

setConsoleSize();

// задание размеров консоли

Sleep(100);

draw(hwnd, hdc);

// функция рисования в консоли

int iKey = 1;

while (iKey != 27) {

// Задержка и выход по клавише ESC

if (_kbhit()) {

iKey = _getch();

switch (iKey)

{

case 112: case 80: case 167: case 135:

draw(hwnd, hdc);

// перерисовка консоли по клавише ‘p’

break;

}

}

}

ReleaseDC(hwnd, hdc);

//освобождаем дескрипторы консоли

}

// Функция рисования. Помещайте сюда всю графику void draw(HWND hwnd, HDC hdc)

{

Rectangle(hdc, 100, 60, 180, 160); // Рисуем прямоугольник

}

void setConsoleSize() // Задание размеров окна консоли

{

const int colConsole = 80; const int rowConsole = 30;

HANDLE hNdl = GetStdHandle(STD_OUTPUT_HANDLE); SMALL_RECT windowSize ={0,0,colConsole-1,rowConsole-1}; SetConsoleWindowInfo(hNdl, TRUE, &windowSize);

COORD bufferSize ={colConsole, rowConsole }; // размеры буфера SetConsoleScreenBufferSize(hNdl, bufferSize);

}

Во второй версии программы мы создали функцию setConsoleSize(.), которая задает размеры консольного окна без полос прокрутки (пока мы не уменьшили размеры окна). Мы также вынесли рисование в отдельную функцию draw(.), которую можем вызывать по нажатию клавиши „p‟, например для перерисовки графики, если она пропала после перекрытия консоли другим окном. Перед завершением программы мы также освободили дескрипторы консоли.

Как вы заметили, графика в консоли видна тогда, когда рисование выпоняется в видимое окно. После сворачивания или перекрытия окна консоли графика исчезает. На самом деле приложения Windows получают сообщение от ОС о том, что они должны перерисовать свое окно (или его часть) и

37

программисты помещают вызов графических функций в обработчик этого сообщения (специальную функцию). В программе, написанной для консоли, мы не можем перехватить и обработать такое сообщение. Но перерисовка консоли после восстановления видимости ее окна все равно происходит. Это приводит к рисованию «пустоты», т.е. к очистке окна. Поэтому нам приходится «выкручиваться». После сворачивания или перекрытия окна консоли рисование нужно повторить. Для этого в предыдущей программе мы создали функцию draw(.), которую могли вызвать клавишей „p‟.

Обратите также внимание на функцию Sleep(.), которую мы вызываем перед вызовом функции draw(.). Она приостанавливает выполнение потока инструкций программы на заданное количество милисекунд. На современных компьтерах, если в коде предыдущей программы не использовать функцию Sleep(.), функция draw(.) успевает выполниться еще до того, как консоль станет видимой на экране. После появления консоли на экране, окно получает сообщение о необходимости перерисовки, и в результате происходит очистка окна. Вставка функции Sleep() перед вызовом функции draw приводит к тому, что окно консоли станет видимым раньше того, как мы что – то нарисуем. Время задержки будет зависеть от быстродействия компьютера. Возможно, вставка функции Sleep()вам не понадобится.

В консольном приложении у нас есть возможность обрабатывать сообщение получения окном фокуса. Это сообщение помещается в буфер событий, который заполняется функцией ReadConsoleInput. Мы использовали эту функцию в примере 8 предыдущего параграфа. Она помещает в буфер событий информацию о мыши, клавиатуре, а также о получении окном фокуса. Если, например, буфер/массив событий наывается eventBuffer, то проверку того, что консоль получила фокус, можно выполнить следующим образом

if (eventBuffer[i].EventType == FOCUS_EVENT) { Sleep(300);

draw(hwnd, hdc);

}

Здесь опять требуется небольшая задержка, покольку рисование может завершитья до того, как окно консоли появится поверх остальных окон. Еще одна версия консольной программы с графикой может выглядеть следующим образом.

Пример 1c. Пример консольной программы с перерисовкой графики при получении окном фокуса.

#include <windows.h>

void draw(HWND hwnd, HDC hdc); void setConsoleSize();

38

void main()

{

HWND hwnd = GetConsoleWindow(); HDC hdc = GetDC(hwnd);

HANDLE rHnd = GetStdHandle(STD_INPUT_HANDLE); SetConsoleTitle(L»Simple Rectangle Drawing»);

setConsoleSize();

Sleep(100);

//

задержка

draw(hwnd, hdc);

//

рисование любого множества фигур

DWORD numEvents = 0; // Количество непрочитанных сообщений DWORD numEventsRead = 0; // Количество прочитанных сообщений bool isRunning = true; // флаг продолжения работы

//Ели isRunning=false, то программа завершается while (isRunning) {

// Определить количество событий консоли

GetNumberOfConsoleInputEvents(rHnd, &numEvents); if (numEvents != 0) {

//выделение памяти для хранения данных о событиях

INPUT_RECORD *eventBuffer = new INPUT_RECORD[numEvents];

//Извлечение данных во временный буфер событий eventBuffer[] ReadConsoleInput(rHnd, eventBuffer, numEvents,

&numEventsRead);

// Цикл по всем извлеченным событиям

for (DWORD i = 0; i < numEventsRead; ++i) { if (eventBuffer[i].EventType == KEY_EVENT) {

if(eventBuffer[i].Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)

isRunning=false; // выход, если нажата клавиша ESC else if(eventBuffer[i].Event.KeyEvent.uChar.AsciiChar==

‘d’)

draw(hwnd, hdc);

// перерисовка по клавише ‘d’

}

if (eventBuffer[i].EventType == FOCUS_EVENT) {

Sleep(300);

draw(hwnd, hdc);

// перерисовка при получении фокуса

}

else if (eventBuffer[i].EventType == MOUSE_EVENT) {

// обработка событий мыши

}

}

delete[] eventBuffer;

}

}

ReleaseDC(hwnd, hdc); //освобождаем дескрипторы

}

Здесь мы используем функцию setConsoleSize(), которую создали в примере 1b, и код которой остался без изменений, а также функцию draw(.), в которую вы можете помещать любые функции рисования.

Изменение размеров окна консоли не приводит к событию FOCUS_EVENT, но вызывает очистку окна. Поэтому мы оставили команду принудительной перерисовки – вызов функции draw при нажатии клавиши „d‟.

39

В программах, которые приведены далее в этом параграфе, для простоты не будет реализован контроль ошибок, не будут освобождаться ресурсы, и графические функции не будут собираться в отдельную функцию (например, draw). Однако в реально функционирующих приложениях соответствующие фрагменты кода обязательны.

Линия может быть жирной и тонкой, прерывистой и штрих – пунктирной, иметь определенный цвет. Это все вместе называется стилем линии (или просто пером). Текущее перо является частью структуры контекста окна. Чтобы линию нарисовать требуемым пером, вначале вы должны создать объект типа HPEN

HPEN Pen = CreatePen(PS_SOLID, 3, RGB(255,0,0));

Первый аргумент функции CreatePen определяет будет ли линия сплошной (PS_SOLID), пунктирной, штрих – пунктирной и т.д. Можно использовать значения PS_DOT, PS_DASH, PS_DASHDOT и некоторые другие. Второй аргумент определяет толщину линии, третий – задает цвет. Аргументы макроса RGB являются целыми числами со значениями от 0 до 255 и задают цвет, составленный из оттенков/яркостей трех базовых цветов: красного (Red), зеленого (Green) и синего (Blue). Нулевое значение исключает соответствующий базовый цвет, максимальное допустимое значение яркости базовых цветов 255.

По умолчанию установлено черное сплошное перо толщиной в 1 пиксель. После создания пера (HPEN) его нужно загрузить в контекст (HDC)

функцией SelectObject(HDC,HPEN). После этого все линии будут рисоваться созданным пером до тех пор, пока в контекст не будет загружено новое перо.

Есть еще один момент. В контексте хранится положение так называемого текущего указателя, который содержит координаты точки от которой следующая графическая функция начинает рисовать свою фигуру. При запуске программы этот указатель устанавливается в точку (0,0). Его местоположение на экране невидимо, он означает лишь позицию в окне для некоторых функций. Не все графические функции используют этот указатель. Функция

MoveToEx((HDC,X,Y,NULL);

переносит текущую позицию указателя в точку (X,Y) и не запоминает старую позицию (если последний аргумент равен NULL).

В следующем примере мы используем функцию

LineTo(HDC,X,Y)

которая рисует отрезок прямой от текущего положения указателя до точки (X,Y) и перемещает указатель в эту точку.

Пример 2.

#include <windows.h> void main()

{

// получаем дескриптор контекста консольного окна

HDC hdc = GetDC(GetConsoleWindow());

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

    23.02.2015231.42 Кб4DS.doc

  • #

Сразу предупрежу – материал очень сырой и некоторые моменты я пропущу. Но это скорее всего все я дополню позднее. Во всяком случае надеюсь, что все получится.

 Итак. Я с этим не сталкивался, но наверное в Универах могут давать лабораторные построения графиков по разным формулам. По большому счету неважно как выглядит формула, все сводится к однообразному построению и у начинающих не такие уж сложные задачи.

 Я выбрал простой график y=x*x Короткая формула, всем известная кривая (всем кто хоть чуть-чуть учился в школе)

 Код C++ Рисование графика
============
#include <graphics.h> // включить описание графических функций С/С++ Borland’a
#include <conio.h>     
// описание функций ввода-вывода с консоли (для getch())

int main()
{
float x, y;
/* Автоматическое определение графических параметров */
int gdriver = DETECT, gmode;
initgraph(&gdriver, &gmode, “”);    
// Инициализация графического режима

x=-10; //Инициализируем x
moveto(x,x*x);
//Устанавливаем курсор

do
{
y=x*x;
//Наша формула для построения графика
lineto(x*50+getmaxx()/2, getmaxy()/2
-(y*20));     // ..откуда рисуем график
x=x+0.02;
} while(x<10);
getch();               
  // ожидание нажатия пользователем любой клавиши
closegraph();       
// выход из графического режима
return 0;
}

============

Что ж. Сразу скажу – Я не знаю почему x увеличивается на дробное число. При моих попытках увеличивать его на единицу график получался очень даже не гладким и это было плохо, поэтому оставил так как отыскал в просторах интернета. Чтобы начать рисовать линию из какой-то точки имеет смысл определить первоначальную точку, для чего была использована функция moveto Сам график строится с помощью циклического вычисления, ведь для каждого значения x нужно получить соответствующий ему y, значит, чтобы не писать все эти выражения вручную нужно использовать цикл.

 Внутри цикла вызывается функция lineto, внутри которой написаны такие параметры, которые могут сбить с толку бедного новичка, но пугаться не стоит. lineto чертит линию от текущей позиции до, но не включая в нее, указанной точки. Другими словами при вычислении значения y мы будем получать некоторые точки, а чтобы получить график, нужно эти точки соединять линиями.
 x*50+getmaxx()/2 Обозначает, что первоначальная точка x смещена к центру экрана по оси x. Цифра 50 здесь нужна только для того чтобы расширить рисуемый график. Нетрудно попробовать её изменить или убрать, чтобы увидеть эффект. Когда я пытался убрать эту 50 и прибавлять к x единицу вместо 0.02 график рисовался ужасно, хотя если подумать, то то что написано здесь или то что хотел написать я сводится к тому что x прибавляется на единицу, но что-то вот в этом есть чего я понять пока что увы не могу. Точнее понимаю, но понимаю как-то туманно, не полностью и сложно для разъяснений

Не буду скрывать, надеюсь на помощь кого-нибудь из читателей, благодаря критике, благодарностям, указанию моих ошибок этот материал может стать намного легче и понятнее. Проверку на ошибки я убрал, чтобы не отвлекало глаза от описываемого кода

Published
August 14, 2012

 

Для рисования графических примитивов в оконных приложениях используются 4 основных типа объектов:

  • точка (Pixel);
  • перо (Pen);
  • кисть (Brush);
  • фон (Background).

Точка

Цвет точки задается с помощью функции

COLORREF SetPixel(
  _In_  HDC hdc,   // дескриптор контекста устройства
  _In_  int X,    // x-координата точки
  _In_  int Y,   // y-координата точки
  _In_  COLORREF crColor ); // цвет точки

В случае удачного завершения возвращаемое значение функции дублирует цвет точки, в случае ошибки возвращает -1.

Цвет точки представляет собой 32-битное число, заданное в системе RGB:
RGB

Можно также воспользоваться функцией

RGB(
  _ Red As Integer,  // красный
  _ Green As Integer,  // зеленый
  _ Blue As Integer);  // синий

Значения красного, зеленого и синего используются в диапазоне 0…255.

Перо

Перо используется для рисования линий и контуров замкнутых фигур. Цвет пера задается функцией

HPEN CreatePen(
  _In_  int fnPenStyle,      // стиль пера
  _In_  int nWidth,      // ширина пера (в пикселях)
  _In_  COLORREF crColor );  // цвет пера

Стили пера fnPenStyle могут быть заданы согласно таблице

 

При успешном завершении функция возвращает дескриптор пера, в случае неудачи — константу NULL.

Кисть

Кисть используется для закрашивания замкнутых объектов. Цвет кисти задается с помощью функции

HBRUSH CreateSolidBrush(
  _In_  COLORREF crColor );   // цвет кисти

При успешном завершении функция возвращает дескриптор кисти, в случае неудачи — константу NULL.
Эта же функция используется для задания цвета фона.

Можно заранее создать несколько кистей и перьев, а затем выбирать нужные с помощью функции

HGDIOBJ SelectObject(
  _In_  HDC hdc,      // дескриптор контекста устройства
  _In_  HGDIOBJ hgdiobj );     // дескриптор объекта

Рисование графических примитивов

Перемещение в указанную точку осуществляется функцией:

BOOL MoveToEx(
  _In_   HDC hdc,    // дескриптор контекста устройства
  _In_   int X,  // координата x точки
  _In_   int Y,    // координата y точки
  _Out_  LPPOINT lpPoint );   // указатель на структуру POINT

Координаты точки x и у определяются в пикселях относительно левого верхнего угла.
В случае успешного выполнения возвращает ненулевое значение.

Структура POINT имеет вид

typedef struct tagPOINT {
  LONG x;
  LONG y; } POINT, *PPOINT;

Рисование отрезков осуществляется функцией:

BOOL LineTo(
  _In_  HDC hdc,       // дескриптор контекста устройства
  _In_  int nXEnd,    // координата x конечной точки
  _In_  int nYEnd );   // координата y конечной точки

В случае успешного выполнения возвращает ненулевое значение.

Рисование прямоугольника осуществляется функцией:

BOOL Rectangle(
_In_  HDC hdc,           // дескриптор контекста устройства
_In_  int nLeftRect,     // x-координата верхнего левого угла
_In_  int nTopRect,      // y-координата верхнего левого угла
_In_  int nRightRect,    // x-координата нижнего правого угла
_In_  int nBottomRect);    // координата нижнего правого угла

Рисование прямоугольника начинается из точки, в которую осуществлено перемещение с помощью функции MoveTo().
В случае успешного выполнения возвращает ненулевое значение.

Рисование эллипса осуществляется функцией:

BOOL Ellipse(
  _In_  HDC hdc,           // дескриптор контекста устройства
  _In_  int nLeftRect,     // x-координата верхнего левого угла
  _In_  int nTopRect,      // y-координата верхнего левого угла
  _In_  int nRightRect,    // x-координата нижнего правого угла
  _In_  int nBottomRect);    // координата нижнего правого угла

Овал
В случае успешного выполнения возвращает ненулевое значение.

Рисование дуги осуществляется функцией:

BOOL ArcTo(
_In_  HDC hdc,   // дескриптор контекста устройства
_In_  int nLeftRect,   // x-координата верхнего левого угла
_In_  int nTopRect,   // y-координата верхнего левого угла
_In_  int nRightRect,  // x-координата нижнего правого угла
_In_  int nBottomRect,   // y-координата нижнего правого угла
_In_  int nXRadial1,   // x- координата конца первого радиуса
_In_  int nYRadial1,   // y- координата конца первого радиуса
_In_  int nXRadial2,   // x- координата конца второго радиуса
_In_  int nYRadial2 );   // y- координата конца второго радиуса

Дуга
В случае успешного выполнения возвращает ненулевое значение.

Вывод текста в окно

Для вывода текста в поле окна используется функция

BOOL TextOut(
_In_  HDC hdc,   // дескриптор контекста устройства
_In_  int nXStart,    // x-координата начала вывода текста
_In_  int nYStart,   // y-координата начала вывода текста
_In_  LPCTSTR lpString,  // указатель на строку текста
_In_  int cchString );  // количество символов для вывода

В случае успешного выполнения возвращает ненулевое значение.

Задать цвет фона под буквами можно с помощью функции

COLORREF SetBkColor(
_In_  HDC hdc,   // дескриптор контекста устройства
_In_  COLORREF crColor );    // цвет

Задать цвет букв можно с помощью функции

COLORREF SetЕTextColor(
_In_  HDC hdc,   // дескриптор контекста устройства
_In_  COLORREF crColor );    // цвет

В случае неудачного завершения эти функции возвращают константу CLR_INVALID=0xFFFF.

Использование графических функций

Для перерисовки окна, в котором будут отображаться графические объекты, будем использовать обработку сообщения WM_PAINT.

Обработка сообщения WM_PAINT почти всегда начинается с вызова функции:

HDC BeginPaint(
  _In_   HWND hwnd,
  _Out_  LPPAINTSTRUCT lpPaint );

При обработке вызова BeginPaint(), Windows обновляет фон рабочей области с помощью кисти, заданной в поле hbrBackground структуры WNDCLASS, описанной здесь. Вызов BeginPaint() делает всю рабочую область действительной (не требующей перерисовки) и возвращает описатель контекста устройства. Контекст устройства описывает физическое устройство вывода информации (например, экран) и его драйвер. Описатель контекста устройства необходим для вывода в рабочую область окна текста и графики.

Аргументы функции:

  • hwnd – дескриптор окна;
  • lpPaint – указатель на структуру PAINTSTRUCT.

Структура PAINTSTRUCT имеет вид

typedef struct tagPAINTSTRUCT {
  HDC  hdc;
  BOOL fErase;
  RECT rcPaint;
  BOOL fRestore;
  BOOL fIncUpdate;
  BYTE rgbReserved[32]; } PAINTSTRUCT, *PPAINTSTRUCT;

Члены структуры:

  • hdc – дескриптор контекста устройства.
  • fErase – ненулевое значение стирает фон.
  • rcPaint – структура RECT, определяющая верхний левый и нижний правый углы рабочей области.

    typedef struct _RECT {
      LONG left; LONG top;
      LONG right; LONG bottom; } RECT, *PRECT;

  • fRestore, fIncUpdate, rgbReserved  – зарезервировано, используется системой.

Обработка сообщения WM_PAINT почти всегда заканчивается вызовом функции:

BOOL EndPaint(
  _In_  HWND hWnd,
  _In_  const PAINTSTRUCT *lpPaint );

Функция EndPaint() освобождает описатель контекста устройства, после чего его значение нельзя использовать. Возвращает всегда ненулевое значение.

Получение дескриптора контекста устройства осуществляется вызовом функции:

HDC GetDC(_In_  HWND hWnd);

hWnd – дескриптор окна, для которого используется контекст устройства.
Возвращаемое значение – дескриптор контекста устройства.

Функция

int ReleaseDC(
  _In_  HWND hWnd,
  _In_  HDC hDC );

освобождает контекст устройства hDC для данного окна hWnd, после чего значение контекста устройства нельзя использовать. Возвращает всегда ненулевое значение.

Пример График функции y=sin(x).

Назад: Создание Windows-приложений

В консоли windows можно рисовать как и в обычном окне. Для этого используется библиотека libgdi. Я использую Qt Creator, и для подключения библиотеки libgdi пришлось прописать в .pro файл проекта путь к libgdi32.a


LIBS += C:\Qt\Tools\mingw810_64\x86_64-w64-mingw32\lib\libgdi32.a

Нарисуем минималистичный рисунок в консоли, пусть это будет белая линия от начала координат (0, 0) в точку (300, 300). По умолчанию, перо которым рисуется в консоли — черного цвета и на черном цвете консоли трудно различимо, поэтому у нас добавляется несколько строчек по созданию белого пера и привязки его к консольному окну. Также возникла ситуация что рисунок появлялся в консоли не при каждом запуске программы, было сделано предположение что рисует быстрее чем заканчиваются вывод консоли на экран и перерисовки консоли стирают рисунок. Предположение подтвердилось путем введения задержки после запуска функцией Sleep(100) и получением стабильной отрисовки.


#include <windows.h>        // GetConsoleWindow(), GetDC()...

int main(){
    Sleep(100);             // Задержка 100 мс, без этого, иногда, перерисовка окна затирает рисунок
    HWND hwnd = GetConsoleWindow(); // Находим дескриптор (handle) консольного окна
    HDC hdc = GetDC(hwnd);  // Находим контекст устройства DC - device context
    HPEN hPen = CreatePen(PS_SOLID, 5, RGB(255, 255,  255));  // Создаем (сплошное перо, толщиной 5 пикселов, белого цвета)
    SelectObject(hdc, hPen);// Привязываем перо к экрану
    LineTo(hdc, 300, 300);  // Рисуем линию от текущих координат (0, 0) к (300, 300)
}

Результат работы программы.
Рисование линии в консоли С++

Усложним задачу и нарисуем толстыми, цветными, градиентными линиями синус.


#include <windows.h>        // GetConsoleWindow(), GetDC()...
#include <iostream>         // std::cout
#include <cmath>            // sin()
#include <cstdlib>          // system()

int main(){
    system("chcp 65001");   // Active code page: 65001 (Устанавливает UTF-8)
    system("cls");          // Очищает окно от предыдущего вывода
    HWND hwnd = GetConsoleWindow();                 // Находим дескриптор (handle) консольного окна
    HDC hdc = GetDC(hwnd);                          // Находим контекст устройства DC - device context
    int x = 0;                                      // Координата x
    for (float i = 0; i < 3.14 * 30; i += 0.05){    // Итерации рисования
        int R = fabs(sin(i * 1.3 - 0.3)) * 255;     // Циклически меняем значение цвета
        int G = fabs(sin(i * 1.1 - 0.5)) * 255;     // Циклически меняем значение цвета
        int B = fabs(sin(i * 1.7 - 0.7)) * 255;     // Циклически меняем значение цвета
        COLORREF color = RGB(R, G,  B);             // Цвет (R, G, B) 0...255 Меняем цвет в процессе
        HPEN hPen = CreatePen(PS_SOLID, 5, color);  // Создаем перо
        SelectObject(hdc, hPen);                    // Привязываем перо к экрану
        int y = 350 - 340 * sin(i);                 // Координата y
        LineTo(hdc, x, y);                          // Рисуем линию
        x += 1;                                     // Увеличиваем x для рисования следующей точки
        DeleteObject(hPen);                         // Освобождаем ресурсы
    }
    ReleaseDC(hwnd, hdc);                           // Освобождаем ресурсы
    std::cout << "Рисуем в консоли";                // Выводим текст
    return 0;
}

Результат работы программы.
Рисование градиентного синуса в консоли С++

2023-09-13

Понравилась страница? Не забудь сохранить и поделиться!

Связанные темы

asm_listing — Ассемблерный листинг С++
benchmark — Бенчмарки в С++
draw_console — Рисование в консоли windows на С++
function — Функции в С++
func_params — Параметры функции в С++
glossary — Глоссарий С++. Идентификаторы, квалификаторы, модификаторы, объявление, определение и т.д..
isklyucheniya — Исключения в С++. Выбрасывание и ловля исключения.
main_page — Достоинства и недостатки C++
no_ide — Сборка приложения без IDE C++ с помощью MinGW и Qt
peregruzka — Перегрузка функций и операторов в С++
random_number — Случайные числа в С++. Полиномиальная генерация случайных чисел.
reference — Ссылки в С++
rekursiya — Рекурсия в С++. Примеры рекурсивных программ и без использования.
templates — Шаблоны C++
var_name — Наименование переменных и стиль программирования
version — Версия компилятора С++
vremya_vypolneniya — Время выполнения программы C++

  • Windows game explorer windows 10
  • Windows forms сортировка массива c
  • Windows gpo disable windows update
  • Windows game controller windows 7
  • Windows forms работа с формами