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!

  • Sajnos még mindig nem működik nálam, pedig átszerveztem a programot úgy, hogy a billentyűleütés figyelések a WM_TIMER-ben, a rajzolások és csakis a rajzolások a WM_PAINT-ben vannak.
    De megpróbáltam a BeginPaint-EndPaint függvénypárost is beszerkeszteni, ám azzal sem működött.
    El tudnád küldeni az általad kiegészített programrészletet, hogy lássam, pontosan hova és mit kell beírni?
    Mutasd a teljes hozzászólást!
  • Az sem úgy működik, ahogy szeretnéd, csak meghívódik a WM_TIMER..
    A probléma a te esetedben, hogy már a WM_PAINT-be INVALIDálod az ablakot, így a folyamatos WM_PAINT hívások miatt kimarad a WM_TIMER.

    HDC-t csak addig kéne használni, amíg létezik. Rövid ideig és mindig újat szerezni belőle. Tehát pl a KisKocka-knak nem kéne a HDC állapotát eltárolni, hanem a kirajzol virtuális függvénynek kéne egy HDC-t fogadnia, hogy na kocka erre rajzold ki magad.

    Így néz ki, ha időzítőre szeretnéd mozgatni:

    case WM_PAINT: { PAINTSTRUCT ps; BeginPaint( hwnd, &ps); Rectangle(ps.hdc,kockax,kockay,kockax+40,kockay+40); EndPaint(hwnd, &ps); } case WM_TIMER: switch (wParam) { case idozito: kockax += 10; InvalidateRect(hwnd, NULL, true); break; };
    Mutasd a teljes hozzászólást!
  • Akkor a WM_PAINT ciklikus mivolta, pontosabban az abban lévő InvalidateRect() függvény állandóan felülírja a WM_TIMER-t? Azt hittem, a kettő egymástól függetlenül hajtódik végre.
    Egyébként nem időzítőre szeretném mozgatni, hanem hogy az időzítő szabályozza az elmozdulás, a kiskockalétrehozás, egyszóval a cselekvések megtörténhetőségét, mert egyébként túl gyorsan mozoghat a nagy kocka és túl sok kiskocka jön létre egymáson, amikor a felhasználó lenyomja a megfelelő gombot.
    Mutasd a teljes hozzászólást!
  • Tomi egy dolgot érts már meg.....

    Nem egyenlő az hogy 10 máspdpercig fut és reagál a billentyűre mit az eszetlen, és az hogy 10 másodpercenként egyszer reagál...
    Ezt próbáltam neked leírni korábban.
    A te verziód kb mint ha a filmet néznéd felgyorsítva 10 másodpercig, majd 10 másodpercre megállítod...
    Mutasd a teljes hozzászólást!
  • Akkor a WM_PAINT ciklikus mivolta, pontosabban az abban lévő InvalidateRect() függvény állandóan felülírja a WM_TIMER-t? Azt hittem, a kettő egymástól függetlenül hajtódik végre.

    Csak hajtódna végre egymástól függetlenül.

    Ismerős fogalom számodra hogy végtelen rekurzív ciklus?
    Ha egy függvény meghívja valamely módon saját magát, és nincs kilépési feltétel, mint  jelen esetedben a VM_PAINT..... akkor esélye se lesz a programodnak mást csinálnia, hiszen az első VM_PAINT üzenet kezelése soha nem fog befejeződni.
    Mivel nem fejeződik be egy üzenet feldolgozása, ezért soha nem fog elkezdődni egy másik üzenet feldolgozása.
    Az üzenetkezelés kritikus, tömör gyors reakció kell hogy legyen, ha mögötte másgt szerenél csináltatni ami több időt vesz igénybe, azt nem szabad az üzenetkezelő függvényben tenni.
    Mutasd a teljes hozzászólást!
  • Egyébként nem időzítőre szeretném mozgatni, hanem hogy az időzítő szabályozza az elmozdulás, a kiskockalétrehozás, egyszóval a cselekvések megtörténhetőségét, mert egyébként túl gyorsan mozoghat a nagy kocka és túl sok kiskocka jön létre egymáson, amikor a felhasználó lenyomja a megfelelő gombot.


    De, pontosan az időzítőre akarod mozgatni...
    Ha nem ezt tennéd akkor a kockák mozgása meg minden egyéb a billentyűzet leütések sebbességétől, vagy a hardver erejétől függene.
    Csak gondolj egy sima PAC-MAN-re... ahol még fel is gyorsul a játék folyamán a sebbesség.
    Ez nem más mind az időzítő módosítása....
    Csak a 10 másodperc szerintem túl nagy...
    A mozgás mértékétől és a kívánt mozgási sebbességtől függően kell kiszámolni a kívánt tik-et.
    Pl azt szeretnéd hogy a kocka  800 pixel széles képen 3 másodperc alatt mozgjon át keresztben és minden egyes mozgatás 10 pixel elmozdulással jár akkor a jó időzítő érték 3/(800/10) = 3/80 = 37 ms
    Mutasd a teljes hozzászólást!
  • Úgy látszik, kicsit fafejű vagyok a C++ logikájához...
    De most írtam egy kis, szintén WinGUI32 alapú kódot direkt az időzítőre fókuszálva, de ebben sem működik, mert mindig az IGAZ értéket írja ki ahelyett, hogy váltogatná a kiírást IGAZ-HAMIS között:

    /* Make the class name into a global variable */ TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp"); const unsigned int idozito1=1; bool cselekedhet=true;

    (...)

    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,idozito1,5000,NULL); break; case WM_TIMER: switch (wParam) { case idozito1: if (cselekedhet==true) { cselekedhet = false; } else { cselekedhet = true; }; break; }; return 0; break; case WM_PAINT: if (cselekedhet==true) { TextOut(grafikakezelo, 50, 10, "CSELEKEDHET: IGAZ", 17); } else { TextOut(grafikakezelo, 50, 10, "CSELEKEDHET: HAMIS", 18); }; return 0; break; case WM_DESTROY: KillTimer(hwnd,idozito1); 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!
  • Szia!

    A WM_PAINT csak akkor fut le ha szükség van rá.
    Az időzítő automatikusan nem hívja meg a WM_PAINT-et.

    A programod ha lefut gondolom csak egyszer írja ki az üzenetet és nem 5 másodpercenként....
    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!
  • Ez is egy jó példaprogram:

    Drawing at Timed Intervals - Win32 apps
    Mutasd a teljes hozzászólást!
  • Juhé, végre működik az időzítő és ezzel a kiírás váltakozása!
    Tehát végül is, ahogy írták már itt, a BeginPaint-EndPaint() függvénypárral kellett kiegészítenem a WM_PAINT eseményt, illetve a WM_TIMER-be rakni az InvalidateRect() függvényt.
    Buta, vagy inkább tájékozatlan voltam, mert azt hittem, a WM_PAINT önműködően frissül mindig.
    Legszívesebben mindannyiótoknak adnám a pontot, mert sokat és türelmesen segítettetek, de mégis Atticsi válaszát jelölöm meg, mert az abban megadott leírásból navigáltam el arra az oldalra, ami felvilágosított.
    Végül még itt a kódrészletem, ami most már jól működik:

    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC grafikakezelo; PAINTSTRUCT ps; switch (message) /* handle the messages */ { case WM_CREATE: SetTimer(hwnd,idozito1,5000,NULL); break; case WM_TIMER: switch (wParam) { case idozito1: if (cselekedhet==true) { cselekedhet = false; } else { cselekedhet = true; }; InvalidateRect(hwnd, NULL, true); break; }; return 0; break; case WM_PAINT: grafikakezelo = BeginPaint(hwnd, &ps); if (cselekedhet==true) { TextOut(grafikakezelo, 50, 10, "CSELEKEDHET: IGAZ", 17); } else { TextOut(grafikakezelo, 50, 10, "CSELEKEDHET: HAMIS", 18); }; EndPaint(hwnd, &ps); return 0; break; case WM_DESTROY: KillTimer(hwnd,idozito1); 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!
  • Csak sikerült...

    Nekem ez az 5 másodpercig cselekedhet utána 5 másodpercig nem akkor sem tetszik...
    Nem az invertálás lesz a jó megoldás ezzel kapcsolatban.
    A timer csak billentsen egy irányba és vagy p vagy egy másik kezelő mozgasson és kérdezze le a gombokat.
    Majd ha ezzel végzett akkor visszabillenti a flag-et.
    Vagy akár ezt a rutint hívja meg a timer direktben és akkor maga ez a flag is felesleges...
    Ami fontos lehet még hogy neked nem bill lenyomás esemény kellhet, hanem jobb lenne lekérdezni a fontos gombok pillanatnyi állapotát.
    getkeystate
    Mutasd a teljes hozzászólást!
  • "Csak sikerült..."

    Igen, hála nektek!

    "Nekem ez az 5 másodpercig cselekedhet utána 5 másodpercig nem akkor sem tetszik..."

    Hát... én is lejjebb vettem annál, hogy a mozgás még simább legyen. Ezt a timer-es megoldást találtam a legegyszerűbbnek a program időzítéses vezérléséhez, hogy a felhasználó által irányított kocka ne száguldozzon annyira. De lehet, az fps vagy a deltatime számítás pontosabb lenne(?).
    Mindenesetre köszönöm, hogy említetted nekem a GetKeyState() függvényt; megnézem, hogy ez mennyire hasznosítható számomra. A lényeg, hogy egyszerre több gomb lenyomása is kezelhető legyen.
    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