Повторный вызов процедуры рисования через Win32 API ничего не выводит
всезнающий ALL!
Уже который день ломаю голову над практической работой - не выходит каменный цветок, т.е. почему-то не рисует другие графики при выборе из меню. Может кто подскажет, что не так у меня? (ChatGPT не смог)
#include <windows.h>
#include <math.h>
// Идентификаторы для пунктов меню
//define IDM_FILE_NEW 1
//define IDM_FILE_OPEN 2
#define IDM_FILE_EXIT 1
#define IDM_HELP_ABOUT 2
// Идентификаторы функций
#define IDM_FUNCTION_01 201
#define IDM_FUNCTION_02 202
#define IDM_FUNCTION_03 203
#define IDM_FUNCTION_04 204
#define IDM_FUNCTION_05 205
#define IDM_FUNCTION_06 206
#define IDM_FUNCTION_07 207
#define IDM_FUNCTION_08 208
#define IDM_FUNCTION_09 209
#define IDM_FUNCTION_10 210
#define IDM_FUNCTION_11 211
#define IDM_FUNCTION_12 212
#define IDM_FUNCTION_13 213
#define IDM_FUNCTION_14 214
#define IDM_FUNCTION_15 215
#define IDM_FUNCTION_16 216
#define IDM_FUNCTION_17 217
#define IDM_FUNCTION_18 218
#define IDM_FUNCTION_19 219
// Идентификатор окна и иконки
#define IDI_APP_ICON "A"
// Идентификаторы содержимого окна "О программе"
#define IDC_PICTURE 101
#define IDC_TEXT 102
#define IDC_OK 103
const double epsilon = 1e-3;
void drawGridAndFunctionGraphics(hwnd, gridLineColor, idFunction);
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
COLORREF gridLineColor = RGB(224,224,224);
switch(Message) {
/* Upon destruction, tell the main thread to stop */
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
case WM_PAINT: {
// drawGrid(hwnd, gridLineColor);
break;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_FILE_EXIT: {
// Обработка нажатия на пункт меню "Выход"
PostQuitMessage(0);
break;
}
// Draw function graph by selection
case IDM_FUNCTION_01: case IDM_FUNCTION_02: case IDM_FUNCTION_03: case IDM_FUNCTION_04:
case IDM_FUNCTION_05: case IDM_FUNCTION_06: case IDM_FUNCTION_07: case IDM_FUNCTION_08:
case IDM_FUNCTION_09: case IDM_FUNCTION_10: case IDM_FUNCTION_11: case IDM_FUNCTION_12:
case IDM_FUNCTION_13: case IDM_FUNCTION_14: case IDM_FUNCTION_15: case IDM_FUNCTION_16:
case IDM_FUNCTION_17: case IDM_FUNCTION_18: case IDM_FUNCTION_19:{
drawGridAndFunctionGraphics(hwnd, gridLineColor, LOWORD(wParam));
break;
}
case IDM_HELP_ABOUT: {
// Обработка нажатия пункта меню "Помощь"
//MessageBox(hwnd, "Выбран пункт меню 'Помощь'", "Сообщение", MB_OK | MB_ICONINFORMATION);
ShowAboutDialog(hwnd);
break;
}
}
/* All other messages (a lot of them) are processed using default procedures */
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
/* The 'main' function of Win32 GUI programs: this is where execution starts */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX wc; /* A properties struct of our window */
HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */
MSG msg; /* A temporary location for all messages */
/* zero out the struct and set the stuff we want to modify */
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc; /* This is where we will send messages to */
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// Загрузка иконки из ресурсов
HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
/* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "WindowClass";
wc.hIcon = LoadIcon(hInstance, IDI_APP_ICON); /* Load a standard icon IDI_APPLICATION*/
wc.hIconSm = LoadIcon(hInstance, IDI_APP_ICON); /* use the name "A" to use the project icon IDI_APPLICATION*/
if(!RegisterClassEx(&wc)) {
MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"WindowClass","Решение нелинейного уравнения методом дихотомии",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, /* x */
CW_USEDEFAULT, /* y */
800, /* width */
600, /* height */
NULL,NULL,hInstance,NULL);
if(hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
// Создание меню
HMENU hMenu = CreateMenu();
HMENU hFileMenu = CreateMenu();
HMENU hFunctionsMenu = CreateMenu();
HMENU hHelpMenu = CreateMenu();
AppendMenu(hFileMenu, MF_STRING, IDM_FILE_EXIT, "Выход");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_01, "Функция №1");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_02, "Функция №2");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_03, "Функция №3");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_04, "Функция №4");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_05, "Функция №5");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_06, "Функция №6");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_07, "Функция №7");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_08, "Функция №8");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_09, "Функция №9");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_10, "Функция №10");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_11, "Функция №11");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_12, "Функция №12");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_13, "Функция №13");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_14, "Функция №14");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_15, "Функция №15");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_16, "Функция №16");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_17, "Функция №17");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_18, "Функция №18");
AppendMenu(hFunctionsMenu, MF_STRING, IDM_FUNCTION_19, "Функция №19");
AppendMenu(hHelpMenu, MF_STRING, IDM_HELP_ABOUT, "О программе");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, "Файл");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFunctionsMenu, "Функции");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hHelpMenu, "Помощь");
// Установка меню для окна
SetMenu(hwnd, hMenu);
/*
This is the heart of our program where all input is processed and
sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
this loop will not produce unreasonably high CPU usage
*/
while(GetMessage(&msg, NULL, 0, 0) > 0) { /* If no error is received... */
TranslateMessage(&msg); /* Translate key codes to chars if present */
DispatchMessage(&msg); /* Send it to WndProc */
}
return msg.wParam;
}
LRESULT CALLBACK AboutDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG: {
// Установка изображения и текста
HBITMAP hBitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_APP_ICON));
HWND hwndPicture = GetDlgItem(hDlg, IDC_PICTURE);
SendMessage(hwndPicture, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
const char* text = "Это пример приложения с использованием WinAPI. Здесь вы можете разместить много текста и изображений.";
HWND hwndText = GetDlgItem(hDlg, IDC_TEXT);
SetWindowText(hwndText, text);
return TRUE;
}
case WM_COMMAND:
if (LOWORD(wParam) == IDC_OK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
BOOL ShowAboutDialog(HWND hwndParent) {
// Регистрация класса окна
WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = AboutDialogProc;
wc.lpszClassName = "AboutDialogClass";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&wc)) {
//MessageBox(hwndParent, "Не удалось создать окно диалога: RegisterClassEx", "Ошибка", MB_OK | MB_ICONINFORMATION);
DWORD dwError = GetLastError();
if (dwError == ERROR_CLASS_ALREADY_EXISTS) {
MessageBox(NULL, "Класс окна уже существует", "Ошибка", MB_OK | MB_ICONERROR);
} else {
MessageBox(NULL, "Ошибка регистрации класса окна", "Ошибка", MB_OK | MB_ICONERROR);
}
return FALSE;
}
// Создание диалогового окна
HWND hDlg = CreateWindowEx(
WS_EX_DLGMODALFRAME, // Расширенные стили окна
"AboutDialogClass2", // Имя класса окна
"О программе", // Заголовок окна
WS_CAPTION | WS_SYSMENU | WS_POPUP | WS_VISIBLE, // Стили окна
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, // Позиция и размеры окна
hwndParent, // Родительское окно
NULL, // Меню окна
GetModuleHandle(NULL), // Дескриптор экземпляра
NULL // Дополнительные параметры создания
);
if (hDlg == NULL) {
return FALSE;
}
// Добавление элементов управления в диалоговое окно
// Код добавления элементов управления остается неизменным
// Отображение и обновление окна
ShowWindow(hDlg, SW_SHOW);
UpdateWindow(hDlg);
// Обработка сообщений для окна
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(hDlg, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return TRUE;
}
double f1(double x) {
return 3*pow(x, 4) - 4*pow(x, 3) - 12*pow(x, 2) + 2;
}
double f2(double x) {
return x - pow(x, 3)/6 + pow(x, 5)/120;
}
double f3(double x) {
double angle = (x * M_PI)/180;
return 3*pow(x, 2) - 8*sin(2*angle) - 2*x;
}
double f4(double x) {
double angle = (x * M_PI)/180;
return -x*pow(x, 3) + 12*sin(3*angle) - 5*x;
}
double f5(double x) {
double angle = (x * M_PI)/180;
return exp(-x) * cos(M_PI*angle);
}
int correctCoord(x, y, winWidth, winHeight) {
return (x >= 0) && (x <= winWidth) && (y >= 0) && (y <= winHeight);
}
void drawGridAndFunctionGraphics(HWND hWnd, COLORREF lineColor, int idFunction) {
PAINTSTRUCT ps;
HDC hdc;
HPEN hPen, oldPen;
COLORREF graphColor;
LPRECT rct;
rct = malloc(sizeof(*rct));
GetClientRect(hWnd, rct);
int winWidth, winHeight, horOffset, vertOffset, cellSize, x, y;
winWidth = rct[0].right;
winHeight = rct[0].bottom;
horOffset = 50;
vertOffset = 50;
cellSize = 5;
float angle, iF;
int midX = winWidth / 2, midY = winHeight / 2;
// коэффициент мастабирования графиков
int mX = 50;
// Преобразуем числа в строку
char buffer[2]; // ????? ??? ???????? ??????
// ?????? ???????????? ?????
hdc = BeginPaint(hWnd, &ps);
// Создаем перо для рисования сетки
//hPen = CreatePen(PS_SOLID, 1, lineColor);
//oldPen = (HPEN)SelectObject(hdc, hPen);
hPen = CreatePen(PS_SOLID, 1, lineColor); //RGB(32,32,32)
SelectObject(hdc, hPen);
int iH, iV, diff, xOffset, gOffset;
for(iV = horOffset; iV < winWidth - horOffset; iV += cellSize) {
MoveToEx(hdc, iV, vertOffset, NULL);
LineTo(hdc, iV, winHeight - vertOffset);
}
for(iH = vertOffset; iH < winHeight - vertOffset; iH += cellSize) {
MoveToEx(hdc, horOffset, iH, NULL);
LineTo(hdc, winWidth - horOffset - (cellSize - 1), iH);
}
// Удаляем старое перо и создаем новое для рисования осей
//DeleteObject(SelectObject(hdc, oldPen));
//hPen = CreatePen(PS_SOLID, 2, RGB(128, 128, 128));
//oldPen = (HPEN)SelectObject(hdc, hPen);
// Карандаш для осей абсцис и ординат
hPen = CreatePen(PS_SOLID, 2, RGB(128,128,128));
SelectObject(hdc, hPen);
// Рисуем ось X
MoveToEx(hdc, horOffset, midY + (cellSize / 2), NULL);
iH = winWidth - horOffset - cellSize;
LineTo(hdc, iH, midY + (cellSize / 2));
LineTo(hdc, iH - cellSize * 2, midY - (cellSize / 2));
LineTo(hdc, iH - cellSize * 2, midY + cellSize);
LineTo(hdc, iH, midY + (cellSize / 2));
sprintf(buffer, "x");
TextOut(hdc, iH - cellSize * 2, midY + cellSize, buffer, 1);//L"Text"
// Рисуем ось Y
MoveToEx(hdc, midX, winHeight - vertOffset, NULL);
LineTo(hdc, midX, vertOffset);
LineTo(hdc, midX - (cellSize / 2), vertOffset + cellSize * 2);
LineTo(hdc, midX + (cellSize / 2), vertOffset + cellSize * 2);
LineTo(hdc, midX, vertOffset);
sprintf(buffer, "y");
TextOut(hdc, midX - (cellSize * 3), vertOffset, buffer, 1);//L"Text"
// Горизонт графиков
iV = midY + (cellSize/2);
for(iF = -4.; iF <= 4.01; iF += .01) {
gOffset = iF * mX;
x = midX + gOffset;
// Шкала координатной сетки
if((int)(iF * 100) % 100 == 0) {
MoveToEx(hdc, x, iV - cellSize, NULL);
LineTo(hdc, x, iV + cellSize);
sprintf(buffer, "%d", (int)iF);
TextOut(hdc, x - cellSize, iV + cellSize * 2, buffer, (iF < 0 ? 2 : 1));//L"Text"
MoveToEx(hdc, midX - cellSize, iV + gOffset, NULL);
LineTo(hdc, midX + cellSize, iV + gOffset);
if((int)(iF * 100) != 0) {
xOffset = iF < 0 ? -1 : 1;
TextOut(hdc, midX + (xOffset * cellSize * 3) - cellSize, iV - gOffset - (cellSize)-1, buffer, (iF < 0 ? 2 : 1));//L"Text"
}
}
switch(idFunction) {
case IDM_FUNCTION_01: {
diff = f1(iF) * mX;
graphColor = RGB(0,0,0);
break;
}
case IDM_FUNCTION_02: {
diff = f2(iF) * mX;
graphColor = RGB(0,0,255);
break;
}
case IDM_FUNCTION_03: {
diff = f3(iF) * mX;
graphColor = RGB(0,255,0);
break;
}
case IDM_FUNCTION_04: {
diff = f4(iF) * mX;
graphColor = RGB(103,6,133);
break;
}
case IDM_FUNCTION_05: {
diff = f5(iF) * mX;
graphColor = RGB(62,42,133);
break;
}
}
y = iV - diff;
if(correctCoord(x, y, winWidth, winHeight))
SetPixel(hdc, x, y, graphColor);
}
// Удаляем перо и завершаем рисование
SelectObject(hdc, oldPen);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
// Обновляем область окна
// InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
Пробовал контекст устройств создавать в оконной процедуре и передавать его в функцию, но не получилось
Ответы (1 шт):
Уберите UpdateWindow(hWnd);
из конца функции рисования.
Функцию рисования напрямую не вызывайте, просто обновите окно.
Логика такая:
Окно должно отрисовывать нужное содержимое по запросу пользователя/программиста (когда нужно обновление содержимого), и по запросу системы (когда окно разворачивается, после изменения размеров, после перекрытия другими окнами и т.д.).
Поэтому рисование вызывается не вручную, а из обработчика события WM_PAINT.
Если вам нужно нарисовать что-то новое, вы обеспечиваете нужные данные, и вызываете мягкое побуждение к перерисовке - например, InvalidateRect, или более жёсткое - ваше UpdateWindow. При этом в очередь сообщений ставится WM_PAINT, и когда до него дойдёт очередь - окно перерисуется.
Если графика сложная, разумно создать битмап, рисовать в нем (выбранном в совместимом контексте), а в обработчике WM_PAINT просто делать BitBlt