Miért nem működik C++-ban az időzítő?

Címkék
Miért nem működik C++-ban az időzítő?
2020-05-19T13:04:42+02:00
2020-05-27T15:36:18+02:00
2022-10-15T21:26:26+02:00
Tomi_T2
Sziasztok!
Az volna a kérdésem, hogy az Időzítő (Timer) az ciklikusan hajtódik végre C++-ban? Tehát beállítok egy időintervallumot, arra végrehajtódik, és aztán újra és újra lefut?
Mert most a programomban beállítottam egy időzítőt, ami vezérelné, hogy elmozdulhat-e gombnyomáskor egy grafikus objektum (enélkül ugyanis nagyon gyors lenne a mozgása), de mintha ez az időzítő nem működne rendesen:

1. Megadom a változót a kód elején:
const unsigned int idozito=1;
bool cselekedhet=true;

2. Itt beállítom 10 másodpercre:
case WM_CREATE:
SetTimer(hwnd,idozito,10000,(TIMERPROC)NULL);
break;

3. Itt pedig használom a cselekedhet változó értékének beállítgatására:
case WM_TIMER:
switch (wParam)
{
case idozito: if (cselekedhet==false) {
cselekedhet = true;}
else {
cselekedhet = false;
};
break;
};
break;

A WM_PAINT-ban mozgatom a kockát, ha a cselekedhet értéke igaz. De bármennyire állítom a SetTimer-ben a számot, ugyanolyan gyors lesz. Ki is íratom a változó értékét:
char szoveg[1];
itoa(cselekedhet,szoveg,10);
TextOut(grafikakezelo, 50, 10, szoveg,5);


és mindig 1-et, azaz igaz-t mutat. Hol és mit rontottam el, hogy ez az érték nem változik 10 mp.-enként igaz és hamis között?
Mutasd a teljes hozzászólást!
A problémádnak nem a C++ -hoz van köze.
Itt egy tutorial, próbáld összehasonlítani az általad írt kóddal egybevetve azzal, amit itt már többen, többféleképpen próbáltunk elmagyarázni.
Különös tekintettel az üzenetek kezelésére és az ablakban való rajzolásra.

Walkthrough: Create a traditional Windows Desktop application (C++)

Ha ez megvan, akkor jöhet ez:

The WM_PAINT Message - Win32 apps
Mutasd a teljes hozzászólást!

  • Ugy a változót nem a windowproc elején deklaráltad, hanem globális?
    Mutasd a teljes hozzászólást!
  • SetTimer function (winuser.h) - Win32 apps

    Nem jól használod.

    Javaslat: Tegyél breakpointot a WM_TIMER casebe és nézd meg, mi a wParam értéke.
    Mutasd a teljes hozzászólást!
  • Itt deklaráltam; tudtommal ez így globális:

    #if defined(UNICODE) && !defined(_UNICODE) #define _UNICODE #elif defined(_UNICODE) && !defined(UNICODE) #define UNICODE #endif #include <tchar.h> #include <windows.h> #include <vector> //A kiskockák vektorba (ami egy tömbtípus) kerülnek, ezért kell ezt itt megadni. /* Declare Windows procedure */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* Make the class name into a global variable */ TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp"); RECT ablak; int kockax=50,kockay=5; const unsigned int idozito=1; bool cselekedhet=true;
    Mutasd a teljes hozzászólást!
  • Így gondoltad? Így nem ír ki semmit:

    case WM_TIMER:
                switch (wParam)
                {
                    char szoveg[1];
                    itoa(wParam,szoveg,10);
                    TextOut(grafikakezelo, 50, 10, szoveg,5);
                    InvalidateRect(hwnd, NULL, true);
    Mutasd a teljes hozzászólást!
  • WM_TIMER esemény feldolgozásakor 0-val vissza kell térni. 
    Minden más (feldolgozatlan) esemény esetén pedig gondoskodni kell az alapértelmezett windowproc meghívásáról. 
    Hátha ez segít..
    Mutasd a teljes hozzászólást!
  • Azért nem látsz semmi kiírást, mert a WM_PAINT-ben érdemes kiírnod, amire kíváncsi vagy. Az InvalidateRect függvénnyel kényszerítheted egy "soron kívüli" WM_PAINT-re.
    Mutasd a teljes hozzászólást!
  • Oké, de a WM_PAINT-ben hogyan íratom ki a WM_TIMER wParam-ét? Mert ugyan a "cselekedhet" és az "idozito" változó értékeit ki tudom, de azok rendületlenül 1-et mutatnak értékként.
    Mutasd a teljes hozzászólást!
  • char szoveg[1]; itoa(wParam,szoveg,10); TextOut(grafikakezelo, 50, 10, szoveg,5); InvalidateRect(hwnd, NULL, true);
    Ezzel a WM_TIMER üzenet wParam értékét írná ki , amely mindig 1 lesz, hiszen az a timer_id -t tartalmazza.  

    WM_TIMER <- ben invalidate ( ahogy atticsi irta )
    WM_PAINT-ben pedig csak rajzolsz, WM_PAINT-ben értelemszerűen nem kell a WM_TIMER semelyik paramétere se.
    Mutasd a teljes hozzászólást!
  • Jé, nekem mindig 0-t mutat a wParam értékeként!
    A kiíratását egyébként a WM_PAINT-ben helyeztem, mert WM_TIMER-ben nem ír ki semmit.
    Mutasd a teljes hozzászólást!
  • Kopizd be a komplett kódot, valami nem kerek. (Ne felejtsd ehhez használni a </> gombot!)
    Mutasd a teljes hozzászólást!
  • Íme:

    #if
    defined(UNICODE) && !defined(_UNICODE) #define _UNICODE #elif defined(_UNICODE) && !defined(UNICODE) #define UNICODE #endif #include <tchar.h> #include <windows.h> #include <vector> //A kiskockák vektorba (ami egy tömbtípus) kerülnek, ezért kell ezt itt megadni. /* Declare Windows procedure */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* Make the class name into a global variable */ TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp"); RECT ablak; int kockax=50,kockay=5; const unsigned int idozito=1; bool cselekedhet=true; class Kiskocka{ public: Kiskocka(int xh,int yh, HDC grafkez) { xhely = xh; yhely = yh; gk = grafkez; } void kirajzol() { Rectangle(gk,xhely,yhely,xhely+10,yhely+10); } private: int xhely,yhely; HDC gk; }; std::vector<Kiskocka> kiskockak; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) { HWND hwnd; /* This is the handle for our window */ MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ /* The Window structure */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */ wincl.style = CS_DBLCLKS; /* Catch double-clicks */ wincl.cbSize = sizeof (WNDCLASSEX); /* Use default icon and mouse-pointer */ wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* No menu */ wincl.cbClsExtra = 0; /* No extra bytes after the window class */ wincl.cbWndExtra = 0; /* structure or the window instance */ /* Use Windows's default colour as the background of the window */ wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; /* Register the window class, and if it fails quit the program */ if (!RegisterClassEx (&wincl)) return 0; /* The class is registered, let's create the program*/ hwnd = CreateWindowEx ( 0, /* Extended possibilites for variation */ szClassName, /* Classname */ _T("Kockamozgató program. Nyilak: mozgás, CTRL: kiskocka létrehozása, SZÓKÖZ: kiskocka törlése."), /* Title Text */ WS_OVERLAPPEDWINDOW, /* default window */ CW_USEDEFAULT, /* Windows decides the position */ CW_USEDEFAULT, /* where the window ends up on the screen */ 640, /* The programs width */ 480, /* and height in pixels */ HWND_DESKTOP, /* The window is a child-window to desktop */ NULL, /* No menu */ hThisInstance, /* Program Instance handler */ NULL /* No Window Creation data */ ); /* Make the window visible on the screen */ ShowWindow (hwnd, nCmdShow); /* Run the message loop. It will run until GetMessage() returns 0 */ while (GetMessage (&messages, NULL, 0, 0)) { /* Translate virtual-key messages into character messages */ TranslateMessage(&messages); /* Send message to WindowProcedure */ DispatchMessage(&messages); } /* The program return-value is 0 - The value that PostQuitMessage() gave */ return messages.wParam; } /* This function is called by the Windows function DispatchMessage() */ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC grafikakezelo = GetDC(hwnd); switch (message) /* handle the messages */ { case WM_CREATE: SetTimer(hwnd,idozito,5000,(TIMERPROC) NULL); break; case WM_PAINT: GetWindowRect(hwnd, &ablak); if (GetAsyncKeyState(VK_LEFT) && kockax-4>0 && cselekedhet==true) {kockax-=4; InvalidateRect(hwnd, NULL, true);}; if (GetAsyncKeyState(VK_RIGHT) && kockax+4+40<(ablak.right-ablak.left) && cselekedhet==true) {kockax+=4; InvalidateRect(hwnd, NULL, true);}; if (GetAsyncKeyState(VK_UP) && kockay-4>0 && cselekedhet==true) {kockay-=4; InvalidateRect(hwnd, NULL, true);}; if (GetAsyncKeyState(VK_DOWN) && kockay+4+40<(ablak.bottom-ablak.top) && cselekedhet==true) {kockay+=4; InvalidateRect(hwnd, NULL, true);}; if (GetAsyncKeyState(VK_CONTROL) && cselekedhet==true) {kiskockak.push_back(Kiskocka(kockax,kockay,grafikakezelo));}; if (GetAsyncKeyState(VK_SPACE) && kiskockak.size()>0 && cselekedhet==true) {kiskockak.erase(kiskockak.begin()); InvalidateRect(hwnd, NULL, true);}; for (int i=0; i<kiskockak.size(); i++) { kiskockak[i].kirajzol(); }; Rectangle(grafikakezelo,kockax,kockay,kockax+40,kockay+40); char szoveg[1]; itoa(wParam,szoveg,10); TextOut(grafikakezelo, 50, 10, szoveg,1); break; case WM_TIMER: switch (wParam) { case idozito: if (cselekedhet==true) { cselekedhet = false; } else { cselekedhet = true; }; break; }; break; case WM_DESTROY: KillTimer(hwnd,idozito); ReleaseDC(hwnd,grafikakezelo); PostQuitMessage (0); /* send a WM_QUIT to the message queue */ break; default: /* for messages that we don't deal with */ return DefWindowProc (hwnd, message, wParam, lParam); } return 0; }
    Mutasd a teljes hozzászólást!
  • Nem akarom nagyon megérteni, de szerintem a WM_PAINT-ben kiadott InvalidateRect okozza az anomáliát. Az minden esetben generál egy újabb WM_PAINT-et, ha nem aszinkron lenne a rendszer, akkor végtelen ciklusnak is nevezhetnénk.
    Mutasd a teljes hozzászólást!
  • De az InvalidateRect() csak bizonyos gombnyomásokra aktivizálódik, meg ki is kommenteltem az előbb őket, és nem történt változás.
    Nekem most az a gyanúm, hogy valami értékadási hiba lehet valahol, valahonnét hiányzik az & előtag vagy ilyesmi. De ez sajnos csak feltételezés...
    Mutasd a teljes hozzászólást!
  • Ha a WM_TIMER-ben csak ez van, akkor mi történik?

    if(cselekedhet) cselekedhet = false; else cselekedhet = true; break;
    Mutasd a teljes hozzászólást!
  • Holnap kipróbálom, mert ma már nincs időm erre.
    De azt is gyanítom, hogy a WM_TIMER le sem fut, mert beleraktam próbaképpen a kiskockacsinálás függvényét (ami a Control gombnál van egyébként), de nem történt meg, pedig kiiktattam mindenféle if-es meg switch-es vizsgálatot, csak az időzítőben volt, és mégsem hajtódott végre.
    Mutasd a teljes hozzászólást!
  • Szia!

    Amint fentebb írták ebbe a részben van a zavar

    case idozito: if (cselekedhet==true) { cselekedhet = false; } else { cselekedhet = true; };
    Ez jelenleg a te időzítőd esetén invertálja a cselekedhet-et.

    Te nem pontosan ezt akartad...

    Ha működni is fog annak az eredménye az lesz hogy 10 másorpercig rohangál, majd 10 másodpercig semmi nem fog történni.

    Át kellene gondolnod mit is szeretnél.
    10 másodpercenként reagálni egyszer?
    Akkor pl áttírni a WM_TIMER részt  úgy hogy ő csak annyit tegyen hogy 10 másodpercenként True-ra állítsa a fleg-et.


    Viszont ha lefut a VM_PAINT ott kell False-ra visszalökni a flag-et.

    Vagy fogod és elfelejted azt az örültséget hogy a VM_PAINT-ban akarsz a rajzoláson kívül bármit is kezelni....
    Mutasd a teljes hozzászólást!
  • Mert ugyan a "cselekedhet" és az "idozito" változó értékeit ki tudom, de azok rendületlenül 1-et mutatnak értékként.

    <br>

    Vajon miért is lesz mindig egy az időzítő?

    const unsigned int idozito=1;

    A cselekedhet is 10 másodpercig igaz lesz majd a következő 10 másodpercben hamis.
    Mutasd a teljes hozzászólást!
  • Lásd:

    A pointer to the function to be notified when the time-out value elapses. For more information about the function, see TimerProc. If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue. The hwnd member of the message's MSG structure contains the value of the hWnd parameter.
    Mutasd a teljes hozzászólást!
  • case WM_TIMER :     // Timer esetén

    MessageBeep (-1) ;      // beep
    fFlipFlop = !fFlipFlop ;  // csinálod itt amit szeretnél.... a WM_PAINT helyett....

    InvalidateRect (hwnd, NULL, FALSE) ;   //// meghívod a rajzolást.

    // majd a végén kilép..... 

    return 0 ;
    Mutasd a teljes hozzászólást!
  • Szia!

    Pontosan azt akarom, hogy 10 mp-enként a cselekedhet változó értéke az ellenkezője legyen: ha igaz volt, akkor hamis, ellenkező esetben igaz.
    Úgy képzelem el ezt az időzítőt, hogy így, ahogy van, a deklarálásakor megadott időtartamonként mindig lefut, azaz újra vizsgálja a WM_TIMER ágat. De valamiért mintha egyszer sem futna le...
    Pedig már így is kipróbáltam:
    cselekedhet = !cselekedhet;
    Akkor azt javaslod, hogy a WM_TIMER-ben rajzoljak a WM_PAINT helyett?
    Mutasd a teljes hozzászólást!
  • Bemásolnád a jelenlegi kódod (fordítható / futtatható változat), akkor gyorsan ránézek..
    Mutasd a teljes hozzászólást!
  • Itt van fentebb az egész kód: https://prog.hu/tarsalgo/209346/miert-nem-mukodik-c-ban-az-idozito?n..

    • 2020.05.20. 17:13
    Mutasd a teljes hozzászólást!
  • Elég sok sebből vérzik sajnos.

    MSDN-en menj végig néhány tutorial-on, sok jó alap példa van. Timer-re is és rajzolásra is.

    A WM_TIMER azért nem hívódik meg, mivel nem dolgozod fel megfelelően a 'WM_PAINT'-et és ezért folyamatosan WM_PAINT-ek hívódik meg (nem kerül validálásra a korábban invalid-nak megjelölt terület)
    Legegyszerűbb, ha a WM_PAINT-ben BeginPaint és EndPaint hívásokat használsz és a PAINTSTRUCT-ba lévő HDC-re rajzolsz.

    Mint írtam sok sebből vérzik a dolog, néhány nem részletezeve:
    * KillTimer-nek a SetTimer által adott visszatérési értéket kell használni
    * Nincs minden getDC hívásnak ReleaseDC párja, hiszen getDC mindig meghívódik, míg a ReleaseDC csak WM_DESTROY esetén
    * Az irányítást célszerűbb lenne úgy megoldani, hogy a billentyű leütéseket figyeled a megfelelő WM message-kel, vagy egy timer X időközönként meghívja az irányító logikát és ott kéred le az Async állapotot. Semmiképpen nem a WM_PAINT-be kéne vizsgálni..
    Mutasd a teljes hozzászólást!
  • Az MSDN leírásokat sokat néztem, de összességében ennyire jutottam vele, mint amennyit a programom mutat.
    A KillTimer-ről is azt írja, hogy a SetTimer-rel beállított és nem a visszaadott értéket kell megadni neki - legalábbis a példákban ezt mutatják.
    De azért megpróbálom átrendezni a kódomat azok alapján, amit írtál, és köszi, hogy szántál időt az átnézésére.
    Mutasd a teljes hozzászólást!
  • SetTimer-el kapcsolatban igazad van.

     An application can pass the value of the nIDEvent parameter to the KillTimer function to destroy the timer.

    Átírtam a kódod és nálam ment, szóval megfogja oldani a dolog.. Begin/EndPaint fontos.
    Mutasd a teljes hozzászólást!
  • Oké, megnézem a begin- és endpaint beépítésével is. De úgy látom, ezek a Paintstruct utasítást is megkövetelik.
    Kicsit szomorú vagyok amiatt, hogy kezd így elveszni a program egyszerűsége, dehát a működőképesség és üzembiztosság a legfontosabb.
    Amúgy ez a WM_TIMER-es dolog ugyanúgy tudja sebességileg szabályozni a programot, mintha az fps értékkel manipulálnék? Egyenrangú a két megoldás?
    Mutasd a teljes hozzászólást!
  • mintha az fps értékkel manipulálnék

    Nem igazán értem, hogy mit értesz ez alatt..
    Sokféleképpen meglehet oldani. Keress rá 'winapi game loop'
    Mutasd a teljes hozzászólást!
  • Valami ilyesmit értek az fps számítás alatt:
    Játékos mozgásának számítása fps szerint c++-ban probléma
    Tehát hogy van "jelenlegi idő" meg "azóta eltelt idő" és az ezekből számított érték az fps.
    De mindenképp rákeresek az általad ajánlott kulcsszavakra.
    Mutasd a teljes hozzászólást!
  • Az delta time, nem FPS.
    Mutasd a teljes hozzászólást!
Címkék
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd