Рисование в консоли c windows h

Графика в консоли C++ Решение и ответ на вопрос 733370

153 / 136 / 45

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

Сообщений: 748

1

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

16.12.2012, 09:09. Показов 77936. Ответов 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

Don’t worry, be happy

17781 / 10545 / 2035

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

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

Записей в блоге: 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



153 / 136 / 45

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

Сообщений: 748

16.12.2012, 11:11

 [ТС]

4

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



0



Don’t worry, be happy

17781 / 10545 / 2035

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

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

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

16.12.2012, 11:26

5

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

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

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

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



0



153 / 136 / 45

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

Сообщений: 748

16.12.2012, 11:32

 [ТС]

6

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

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

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



1



Эксперт С++

3645 / 1377 / 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



Эксперт С++

3645 / 1377 / 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



Don’t worry, be happy

17781 / 10545 / 2035

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

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

Записей в блоге: 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

Don’t worry, be happy

17781 / 10545 / 2035

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

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

Записей в блоге: 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



Don’t worry, be happy

17781 / 10545 / 2035

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

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

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

01.09.2013, 15:31

17

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

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

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

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

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

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

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

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

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

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

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

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

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



1



Модератор

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

8759 / 6549 / 887

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

Сообщений: 22,971

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

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

 

Для рисования графических примитивов в оконных приложениях используются 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-приложений

#include <iostream> #include <windows.h> using namespace std; struct IMAGE { RGBTRIPLE** image; int width; int height; }; IMAGE load_image(const char* filename) { FILE* f; fopen_s(&f, filename, «r»); if (!f) { cout << «wrong open for reading filen»; exit(0); } BITMAPFILEHEADER header = {}; BITMAPINFOHEADER info = {}; fread(&header, sizeof(header), 1, f); fread(&info, sizeof(info), 1, f); IMAGE img {}; img.width = info.biWidth; img.height = info.biHeight; img.image = new RGBTRIPLE* [img.height]; fseek(f, header.bfOffBits, SEEK_SET); unsigned char* massiv = new unsigned char[img.width * img.height * sizeof(RGBTRIPLE)]; fread(massiv, 1, img.width * img.height * sizeof(RGBTRIPLE), f); for (int i = 0; i < img.height; i++) { img.image[img.height — 1 — i] = new RGBTRIPLE[img.width]; memcpy(img.image[img.height — 1 — i], massiv + i*img.width*sizeof(RGBTRIPLE), img.width*sizeof(RGBTRIPLE)); } fclose(f); delete[]massiv; return img; } void show_image(IMAGE img, int x, int y) { HDC hdc = GetDC(GetConsoleWindow()); for (int h = 0; h < img.height; h++) { for (int w = 0; w < img.width; w++) { auto & color = img.image[h][w]; SetPixel(hdc, w + x, y + h, RGB(color.rgbtRed, color.rgbtGreen, color.rgbtBlue)); } } } void main() { system(«mode con cols=120 lines=50»); system(«title Bitmap picture in console!»); // можно указать любой существующий путь к bmp-файлу, // но — он должен быть 24-разрядный, // и ширина картинки должна быть кратна 4!!! IMAGE image = load_image(«C:\pictures\some_bmp_file.bmp»); show_image(image, 20, 20); // 20 20 — shift from top left corner of console window system(«pause»); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // very simple animation example: ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include <iostream> #include <windows.h> #include <conio.h> #include <string> using namespace std; #pragma comment(lib,»msimg32.lib») struct IMAGE { RGBTRIPLE** image; int width; int height; }; IMAGE load_image(const char* filename) { FILE* f; fopen_s(&f, filename, «r»); if (!f) { cout << «wrong open for reading filen»; exit(0); } BITMAPFILEHEADER header = {}; BITMAPINFOHEADER info = {}; fread(&header, sizeof(header), 1, f); fread(&info, sizeof(info), 1, f); IMAGE img{}; img.width = info.biWidth; img.height = info.biHeight; img.image = new RGBTRIPLE*[img.height]; fseek(f, header.bfOffBits, SEEK_SET); unsigned char* massiv = new unsigned char[img.width * img.height * sizeof(RGBTRIPLE)]; fread(massiv, 1, img.width * img.height * sizeof(RGBTRIPLE), f); for (int i = 0; i < img.height; i++) { img.image[img.height — 1 — i] = new RGBTRIPLE[img.width]; memcpy(img.image[img.height — 1 — i], massiv + i*img.width * sizeof(RGBTRIPLE), img.width * sizeof(RGBTRIPLE)); } fclose(f); delete[]massiv; return img; } void DrawBitmap(string path, int left_shift, int top_shift, int width, int height) { int style = WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | SS_BITMAP; HWND img = CreateWindowExA(0, «static», 0, style, left_shift, top_shift, 0, 0, GetConsoleWindow(), (HMENU)0, GetModuleHandle(0), 0); HBITMAP bitmap = (HBITMAP)LoadImageA(0, path.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); // auto-adjust width and height if (width || height) bitmap = (HBITMAP)CopyImage(bitmap, IMAGE_BITMAP, width, height, LR_COPYRETURNORG); SendMessage(img, (UINT)STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bitmap); if (width || height) SetWindowPos(img, HWND_TOP, left_shift, top_shift, width, height, SWP_DRAWFRAME); } void show_image(IMAGE img, int x, int y) { HDC hdc = GetDC(GetConsoleWindow()); for (int h = 0; h < img.height; h++) { for (int w = 0; w < img.width; w++) { auto & color = img.image[h][w]; SetPixel(hdc, w + x, y + h, RGB(color.rgbtRed, color.rgbtGreen, color.rgbtBlue)); } } } void options() { SetConsoleTitleA(«Animation Example»); system(«mode con cols=15 lines=50»); HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cci; cci.bVisible = 0; cci.dwSize = 100; SetConsoleCursorInfo(h, &cci); } int posX = 40; int posY = 10; int pause = 80; int speed = 5; void main() { options(); IMAGE one = load_image(«D:\1\1.bmp»); IMAGE two = load_image(«D:\1\2.bmp»); IMAGE three = load_image(«D:\1\3.bmp»); IMAGE clear = load_image(«D:\1\0.bmp»); while (true) { show_image(one, posX, posY); Sleep(pause); show_image(clear, posX, posY); posY += speed; show_image(two, posX, posY); Sleep(pause); show_image(clear, posX, posY); posY += speed; show_image(three, posX, posY); Sleep(pause); show_image(clear, posX, posY); posY += speed; show_image(two, posX, posY); Sleep(pause); show_image(clear, posX, posY); posY += speed; } Sleep(INFINITE); }

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


  1. iamlamer

    iamlamer

    New Member

    Публикаций:

    0

    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia

    В http://www.firststeps.ru/mfc/winapi/r.php?6 написано, что сабж возможен, а нифига не рисует.

    1.    // Ищем консольное окошко с именем GRAFCON
    2.    if (v.dwPlatformId==VER_PLATFORM_WIN32_NT)
    3.            hwnd=FindWindow(«ConsoleWindowClass»,»C:grafcon.exe»);
    4.            hwnd=FindWindow(«tty»,»GRAFCON»);
    5.    if (!hwnd) printf(«nWindow not found %u», GetLastError());
    6.    // По хэндлу получаем контекст
    7.    if (!hdc) printf(«nGet context error %u», GetLastError());
    8.    // Рисуем серенький квадратик
    9.        cr1=SetPixel(hdc, x, y, RGB(128, 128, 128) );
    10.        cr2=SetPixel(hdc, 150-x, y, RGB(128, 128, 128) );
    11.        if ((cr1==-1)||(cr2==-1)) { printf(«nSetPixel error %u», GetLastError()); goto ex; }

    В Win98 выдает странную ошибку «SetPixel error 0» и вообще ничего не рисует.
    В Win2K не находит окошка, а квадратик все-таки рисует, но на раб. столе.

    Ни се во ни па ни маю. :dntknw:

    Где я накосячил?
    Или пните туда, где эта тема обсуждалась (что-то на васме не нашел).


  2. redsh

    redsh

    New Member

    Публикаций:

    0

    Регистрация:
    19 июл 2007
    Сообщения:
    20

    Консоль поддерживается на уровне ядра и не имеет отношения к GDI, поэтому рисовать в ней не получится (также, как создавать контроллы).

  3. Консоль ядром не поддерживается! Консоль — обычное окно с обычными свойствами, просто принадлежит csrss.
    GetWindowDC используй, когда-то давно я это пробовал.


  4. roman_pro

    roman_pro

    New Member

    Публикаций:

    0

    Регистрация:
    9 фев 2007
    Сообщения:
    291

    Ничего сложного, вот код, всё работает:

    1. TCHAR Str[]=_T(«TestString»);
    2.     TCHAR UniqueTitle[]=_T(«some_unique_value»);
    3.     TCHAR OldTitle[MAX_PATH]={0};
    4.     GetConsoleTitle(OldTitle, MAX_PATH);
    5.     SetConsoleTitle(UniqueTitle);
    6.     HWND hConsWnd=FindWindow(NULL, UniqueTitle);
    7.     SetConsoleTitle(OldTitle);
    8.             SetTextColor(hDC, RGB(255,0,0));
    9.             TextOut(hDC, 0, 0, Str, lstrlen(Str));
    10.             MoveToEx(hDC, 0,0, NULL);
    11.             ReleaseDC(hConsWnd, hDC);
    12.     MessageBox(0,_T(«End»),0,0);

  5. iamlamer

    iamlamer

    New Member

    Публикаций:

    0

    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia

    Ура! Спасибо roman_pro !

    Вот такой вариант частично заработал:

    1.    // Принудительно называем окно консоли «grafcon»
    2.    SetConsoleTitle(«grafcon»);
    3.    // Ищем консольное окошко с именем grafcon
    4.    v.dwOSVersionInfoSize = sizeof(v);
    5.    if (v.dwPlatformId==VER_PLATFORM_WIN32_NT)
    6.            hwnd=FindWindow(«ConsoleWindowClass»,»grafcon»);
    7.           hwnd=FindWindow(«tty»,»grafcon»);
    8.    // По хэндлу получаем контекст
    9.    // Рисуем серенький кводратег
    10.        cr1=SetPixel(hdc, x, y, RGB(128, 128, 128) );
    11.        cr2=SetPixel(hdc, x, y, RGB(128, 128, 128) );
    12.        if ((cr1==-1)||(cr2==-1)) { printf(«nSetPixel error %u», GetLastError()); goto ex; }

    Но только в Windows 2K и Windows XP, а в Windows 98 по прежнему ничего не получаеццо. :dntknw: А мне, хоть застрелись, надо, чтобы рисовалось везде. :dntknw:

    Вот здесь http://www.firststeps.ru/mfc/winapi/r.php?6 же явно Маздай 98. Чего там автор с пером мутил? Это очепятка что ли, должно было быть CreatePen?


  6. iamlamer

    iamlamer

    New Member

    Публикаций:

    0

    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia

    Урррра!!! Все заработало!!! Оказываеццо, Windows NT, 2000, XP пофигу, а Windows 95, 98, ME надо именно GetWindowDC, а не GetDC !!!

    Вот финальный, работающий везде вариант:

    1. // Рисование в окне консоли
    2.    // Принудительно называем окно консоли «grafcon»
    3.    SetConsoleTitle(«grafcon»);
    4.    // Ищем консольное окошко с именем grafcon
    5.    v.dwOSVersionInfoSize = sizeof(v);
    6.    if (v.dwPlatformId==VER_PLATFORM_WIN32_NT)
    7.     hwnd=FindWindow(«ConsoleWindowClass»,»grafcon»);
    8.     hwnd=FindWindow(«tty»,»grafcon»);
    9.    // По хэндлу получаем контекст
    10.    for (x=50;x<100;x++) for (y=50;y<100;y++) SetPixel(hdc, x, y, RGB(128, 128, 128) );

WASM

Время прочтения
2 мин

Просмотры 37K

В последнее время Хабр просто пестрит «30-строчными постами»:

— Гоночка на JavaScript (30 строк кода)
— Крошечный арканоид на JavaScript (30 строк кода)
— Крошечный Excel на чистом JavaScript (30 строк кода)
— Крошечная змейка на JavaScript (30 строк кода)
— Tetris на javascript (в 30+ строк)

Одержимый желанием следовать моде, я тоже решил что-нибудь написать. Но так как с JS у меня отношения сложные, я решил писать на C++ с использованием API, которое так любезно предоставила мне ОС Windows (в народе именуемом WinAPI).

Итак, ближе к

телу

делу:

C++ код (31 строка):

#include <Windows.h>
#include <vector>
#include "resource.h"
BOOL CALLBACK PaintProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
	POINT t; static std::vector<POINT> pos; HDC hdc; static bool status; PAINTSTRUCT pt;
	switch(uMsg) {
	case WM_INITDIALOG: status = false;
		return TRUE;
		break;
	case WM_PAINT: hdc = BeginPaint(hwnd, &pt);
		(!pos.size()) ? NULL : MoveToEx(hdc, pos[0].x, pos[0].y, NULL);
		for(size_t i=0;i<pos.size();++i)
			(pos[i].x==-1 && i<pos.size()-1) ? MoveToEx(hdc, pos[i+1].x, pos[i+1].y, NULL) : LineTo(hdc, pos[i].x, pos[i].y);
		EndPaint(hwnd, &pt);
		break;
	case WM_LBUTTONDOWN: status = true;
		break;
	case WM_LBUTTONUP: status = false;
		t.x=-1; pos.push_back(t);
		break;
	case WM_MOUSEMOVE: GetCursorPos(&t);
		ScreenToClient (hwnd, &t);
		(status) ? pos.push_back(t) : NULL;
		(status) ? InvalidateRect(hwnd, NULL, true) : NULL;
		break;
	case WM_CLOSE: EndDialog(hwnd, 0);
		break; }
	return FALSE; }
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR lpCmdLine, int nCmdShow)
{   DialogBox(hInst, MAKEINTRESOURCE(IDD_PAINT), NULL, PaintProc);
	return 0; }

Так как меряемся

пиписьками

основным кодом, не учитывая «верстку», описание диалога, как ресурса, не учитываю, но желающим воспроизвести данный пример необходимо создать диалог с ID — IDD_PAINT.

Собственно, то, ради чего всё начиналось:

image

Особенности

— 31 строка C++

говно

кода
— Использованные библиотеки: STL

Недостатки

— Мерцание экрана при непосредственном рисовании (двойная буферизация была опущена в силу экономии строк).
— В реальной задаче написания программы (аля Paint) использование контейнера vector, возможно, не самый лучший вариант, но все же.

Итог

Согласен, немного неправильно укладывать объявление переменных в одну строку. Но думаю, что на общее впечатление от кода это не повлияет.
Всем спасибо за внимание. +1 к общему количеству «ненормальных».

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

  • #

Like this post? Please share to your friends:
  • Рейтинг файловых менеджеров для windows 10
  • Ремонтный диск для windows 10 скачать бесплатно
  • Рейтинг утилит для оптимизации windows 10
  • Ремонтная флешка для windows 7 скачать торрент
  • Рейтинг торрент клиентов для windows 10