Ciklus megállítása és folytatása gombnyomásra Pythonban
2019-01-29T16:34:45+01:00
2019-02-06T19:45:21+01:00
2022-08-11T09:55:30+02:00
Lukács Tibor
Sziasztok!
Egy sakkprogramot fejlesztgetek python+tkinter-ben amatőrként amolyan hobby project célzattal. Gondoltam, tanulási folyamatnak is jó lesz. A program tud lejátszani txt fájból, ahol egy listában meg lehet adni a figura azonosítóját és a koordináta növekményeket, illetve szabványos pgn fájlból, ha az csak egy mérkőzést tartalmaz. A pgn lejátszáshoz szeretnék egy olyan funkciót, hogy a lejátszás gomb újra megnyomására a lépés szüneteljen, majd ismételt megnyomásra újra elinduljon. Ezenkívül két külön gombbal vezérelném a lépésenkénti lejátszást ill. visszalépést. Keresgéltem a neten, de nem találtam arra megoldást, hogy hogyan lehet egy futó ciklust gombnyomással megállítani, utána pedig folytatni. Ehhez kérném a segítségeteket, ötleteket. Mellékelem a gui képét és a pgn lejátszó rutint.
Előre is köszönöm!
Mutasd a teljes hozzászólást!
Csatolt állomány
Szia!

Nem vágom ezt a nyelvet, de gondolom itt is van Timer. A timert rákötöd arra a rutinra ami léptet. Előre, meg hátra. Ok, ezzel automatizáltad is a dolgot ciklus nélkül, mert a timer megoldja. (adott időközönként meghívja ezt a rutint)

Na most kell egy globális logikai változó, kezdetben True értékkel. Stop gomb megnyomásakor ez a változó false-ra átállít. Utána annak a rutinnak a meghívása amit a timer hívogat folyton. Abban a rutinban van egy feltétel, ami ezt a változót figyeli egy sima if-el és ha false, akkor nem csinál semmit....

ha valami változó= true 
    ide az eddigi kód

he vége

Az indít gombnál ez a változó true-ra állít, megint meghív a magfelelő rutin és kész is. Az előre/hátra irányt hasonlóan megoldhatod.

Itt a timer objektum használatára példa:

7.5.7 Timer Objects

Rá is van aggatva egy rutin.
Mutasd a teljes hozzászólást!

  • Alább csatoltam a GUI képet is.
    Mutasd a teljes hozzászólást!
    Csatolt állomány
  • Hát ez nem egy valós és követendő cél, hogy a futó ciklust megállítsd. Túl sok lehetőséged sincs, mondjuk kilövöd az adott szálat, de hát nem ez a cél.

    Inkább gondolkodj másként: az automata lejátszó (úgyis időzítve lép, hogy az ember tudja követni), minden lépés előtt megnézi, hogy meg kell-e állnom. Ha igen, akkor az állapotát elmentve megáll. 
    Ha megnyomják a folytat gombot, akkor az adott állapottól elkezdi a lejátszást.

    Azaz kell neked egy lejátszó, ami adott lépéstől le tudja játszni a txt-ben megadott meccset. Ez addig fut amíg vége nincs, vagy amíg a lépés elején azt nem találja hogy megállították. Ha megállították, akkor visszaadja hanyadik lépésnél járt (x).

    A folytat az adott lépéssel (x) indítja az animációt.

    A lejátszás indítása meg 0-van indítja az animációt.

    Na jó, bele lehet kötni hogy a ciklust megállítod, de ez már inkább egy if, vagy nem előírt lépésszámú hátul/elöl tesztelő ciklus :)
    Mutasd a teljes hozzászólást!
  • Szia!

    Nem vágom ezt a nyelvet, de gondolom itt is van Timer. A timert rákötöd arra a rutinra ami léptet. Előre, meg hátra. Ok, ezzel automatizáltad is a dolgot ciklus nélkül, mert a timer megoldja. (adott időközönként meghívja ezt a rutint)

    Na most kell egy globális logikai változó, kezdetben True értékkel. Stop gomb megnyomásakor ez a változó false-ra átállít. Utána annak a rutinnak a meghívása amit a timer hívogat folyton. Abban a rutinban van egy feltétel, ami ezt a változót figyeli egy sima if-el és ha false, akkor nem csinál semmit....

    ha valami változó= true 
        ide az eddigi kód

    he vége

    Az indít gombnál ez a változó true-ra állít, megint meghív a magfelelő rutin és kész is. Az előre/hátra irányt hasonlóan megoldhatod.

    Itt a timer objektum használatára példa:

    7.5.7 Timer Objects

    Rá is van aggatva egy rutin.
    Mutasd a teljes hozzászólást!
  • Szerintem ez nem ilyen egyszerű, legalábbis ebben az estben. Itt a start_pgn szubrutinon belül elég sok minden van. Először a pgn fejléceit vizsgálja, ami szerint majd kiírja a kijelzőre is. Nyilván ezt csak egyszer kell megcsinálni. A ciklus csak ezután jön, ami a lépést generálja. S akkor itt nem az egész rutint kellene timer-ezni csak benne lévő ciklust. De hogy azt hogy lehet fogalmam sincs.
    Mutasd a teljes hozzászólást!
  • A png-t is le kell gyártani újra nem? Az egész timerből hívott rutint lehet hívogatni. A Phyton-ban is van exit function, vagy exit sub, vagy valami. Az egész újrarajzolás előtt a logikai változó figyelése, kilépéssel, ha nem kell csinálni semmit.

    Valójában nem annyira bonyolult. A ciklus mit csinál pontosan?
    Mutasd a teljes hozzászólást!
  • A ciklus a pgn fájból veszi lépéssorszámokat és a lépéseket. Egyrészt végzi a lépések generálását a python-chess külső modul segítségével, másrészt a lépések kijelzőre való kiíratását is vezérli. A lépéseknél először megállípítja, hogy honnan lépett arra a helyre a figura, majd ezekből  a két mező kordinátáit, azokból meg a dx, dy különbségeket. A léptetéshez a figura azonosítója (egy integer) és a dx, dy kell. Utána ellenőrzi, hogy van-e ütés, azon belül, hogy az en passant ütés-e. Szóval eléggé összetett.
    Mutasd a teljes hozzászólást!
  • Valahogy nem igazán értem, hogy itt mi szerepe lenne a timer-nek. A timer - gondolom - csak annyit csinál, hogy bizonyos idő után meghív egy függvényt, szubrutint. Itt viszont egy listában lévő lépések sorozatáról van szó, ami csak a listán való végighaladással, tehát ciklussal lehet megoldani. Ráadásul ugyanazzal a gombbal kellene indítani, megállítani, majd folytatni. Szóval még mindig azt nem értem, ha elindítom a ciklust és mondjuk a 10. lépés után szeretném megállítani, majd utána a 11. lépéssel folytatni, akkor ez hogy megy. Ha break-et teszek bele, az kiugrik a ciklusból, akkor ugyanonnan már nem tudja folytatni. Vannak a Python-ban úgynevezett iterátorok meg generátorok, lehet hogy ezekkel kellene operálni és kombinálni a timer-rel. De ezekben abszolút nem vagyok jártas.
    Mutasd a teljes hozzászólást!
  • Csak egy ötlet volt.

    Azonban a timer maga a ciklus. Abban a rutinban amit meghív nyugodtan lehet egy számláló ami a ciklusváltozó szerepét tölti be. Azaz mindig növeled 1-el. (szintén globális változóval.) A ki be kapcsolgatás akkor meg egy globális boolean változóval, ami a gomb megnyomásakor negálódik. Sőt egy másik gombbal megoldhatod, hogy visszafelé lépdessen.

    Lehet félre értettem a dolgot, de azt gondoltam, hogy a ciklus sem 1-2ms alatt fut le, hanem van benne valami késleltetés. Ezt arra alapoztam, hogy meg kell tudni gombbal akasztani.

    De különben meg lehet szakítani úgy is ahogy akarod, sima if-el. egyszerűen kilépteted a rutinból, vagy a ciklusból. Újra is indíthatod, mert a gomb megnyomásakor megint meghívod azt a rutint amiben a ciklus van.

    Azaz a gombban:

    logikai_valtozo= NOT logikai_valtozo

    ha logikai_valtozo=True

    meghívom megint azt a rutint.

    különben nem.
    Mutasd a teljes hozzászólást!
  • A programod nem látom, de gondolom jobb lenne darabokra szedni több kisebb rutinokra.

    Többet nem tudok segíteni, majd más esetleg.

    Most látom, hogy van forráskód is. Majd ránézek, eddig elkerülte a figyelmem. Bocsánat!
    Mutasd a teljes hozzászólást!
  • Köszönöm szépen eddigi segítségedet! Még nem látom a megoldást, de alszom rá egyet, aztán majd holnap fríssen újra nekiugrok. Igen, mellékeltem a forráskódnak azt a részét, ami pgn lejátszást tartalmazza. Azóta ezt két szubrutinra osztottam, hogy külön legyen csak a ciklust tartalmazó rész. A Gui-ról is van képernyőkép mellékelve.
    Mutasd a teljes hozzászólást!
  • A timer ott kellene, hogy ne legyen sleep(2) a kódodban. zizikus is ugyanazt mondja: nincs ciklus, írd át úgy az egészet, hogy egy időzítő futtatja 2 másodpercenként, hogy most lépj.

    azaz a for i, mov in ...

    részt kelelne egy függvénybe kiszervezned, amit mondig a timer hív, hogy lépj +1 -t.
    elindul a  függvény, és a korábbi állapot alapján (amit eltárolt valami nem lokális változóban) meglépi a lépést, rajzol, majd befejezi a futását.

    ha ez megvan, akkor majd csak az eléjére kell beleraknod egy vizsgálatot, hogyha user megnyomta a megáll gombot, akkor mégse fuss le.

    eseményvezéreltre kéne átszerelned. a sleep(2)-vel durván fogod a procit, amíg nem csinál semmit a program, gondolom te sem szereted azokat a programokat amik nem reagálnak a gombokra, ha csinálnak valamit "befagynak".

    ami csak a listán való végighaladással, tehát ciklussal lehet megoldani.

    nem:
    akt_lepes = 0;

    funkcio lep() {
     akt_lepes alapján lépés kirajzolása
     akt_lepes++;
     return;
    }

    set_timer(lep, 2 sec)

    ugyanúgy végig fog menni, és nem eszi a processzort a sleep, illetve meg is tudod állítani bármikor
    Mutasd a teljes hozzászólást!
  • Köszönöm szépen  a te segítségedet, ötleteidet is! Ma megpróbálom átrágni, végiggondolni amit írtatok, aztán majd meglátjuk, hogy sikerül-e ebből működő programot összehozni. Jó lenne, mert a neheze még csak ezután jön. Valamiféle sakk engine írása, hogy lehessen a gép ellen játszani. Persze ezt még megelőzi, hogy egyáltalán én tudjam mozgatni a bábukat egérrel  a táblán, stb.
    Mutasd a teljes hozzászólást!
  • Megcsináltam Timer-rel, a lépésenkénti lejátszás megy is, ha a  > gombot nyomogatom. De most meg a folyamatos lejátszás nem megy a Play game gomb lenyomására. Ott is csak egy lépést játszik le és megáll. Egyszerűen nem látom be, hogy a folyamatos lejátszást hogy lehet ciklus nélkül megvalósítani. Ha meg ciklussal van, akkor meg megállítani nem lehet. Örülnék egy egyszerű példának Python nyelven.
    Mutasd a teljes hozzászólást!
  • Fel tennéd az új kódot is ami Timerrel megy?
    Mutasd a teljes hozzászólást!
  • Üdv, nem értek a pythonhoz, de itt egy butított példa:

    FrizzyDeepEngineers

    forráskód ha nem menne a link:

    import location import os import time import threading move_list = ['b1', 'w1', 'b2', 'w2', 'b3', 'w3', 'b4', 'w4'] canStep = True; iterator = 0; isEnded = False; #Timer osztály ami saját magát hívogatja class RepeatTimer(threading.Timer): def run(self): while not self.finished.wait(self.interval): self.function(*self.args, **self.kwargs) def step(): global iterator if (iterator < len(move_list)): if canStep == True: print(move_list[iterator]) iterator = iterator + 1 else: timer.cancel() global isEnded isEnded = True print ('---END---') timer = RepeatTimer(1, step) timer.start() while isEnded != True: input("Press any to start/stop\n") canStep = not canStep print ('---START---')
    Mutasd a teljes hozzászólást!
  • Köszönöm, megnézem!
    Mutasd a teljes hozzászólást!
  • Csatoltam az új kódot. Ez egyelőre úgy működik, hogy a Play Game gombbal íindítom, de az csak annyit csinál, hogy kiírja az első lépés sorszámát, majd megáll (hiszen nincs benne ciklus, ami tovább vinné). De ha azután megnyomom újra és újra a > gombot, akkor az előre lejátszás lépésenként működik. (A visszajátszással még nem foglalkoztam). Most azt kellene valahogy megoldani, hogy menjen a folyamatos lejátszás is ill. ugyanazon gomb megnyomására a megállítás, ismételt megnyomásra a lejátszás folytatása. Mint a zenelejátszókon a play/pause gomb. Egyelőre még probléma az is, hogy a fájlból parse-olt lépéslistában nem csak a lépések vannak, hanem a lépések sorszámai is. Így ha ix =0 vagy hárommal osztható, akkor nem lépést dolgoz fel, hanem lépésszámot ír ki a kijelzőre. A Timer-es lejátszásnál ez egy kicsit gond, mert minden harmadik Time hívásnál nem lesz lépés, csak kiíratás. Valószínű át kell dolgozni a move_list-et, hogy csak lépés sztringek legyenek benne.
    Mutasd a teljes hozzászólást!
    Csatolt állomány
  • Nem értek ehhez a nyelvhez, de azért látszik mit kellene csinálnia.

    1. A timer-indításakor   play_pgn  hozzá van kötve, gondolom az a gomb, és ez a rutin lefut, hogy elindítsa egyáltalán a timer-t

    2. mov = move_list[ix] Ok, de hol változik ix??? Ez lett volna korábban a ciklusváltozó szerintem. 

    Ennek nem  a forward ban kell növekednie!!!!!! Hanem a timer által hívott play_pgn() rutinban!

    def play_pgn(): ix += 1 # Itt most mennie kell, de nem lesz jó, mert rögtön 1-el kezd majd nem 0 val. Ezt elkerülendő ennek a rutinnak a végére kell tenni mov = move_list[ix] # Az aktuális lépés a lista ix-dik eleme if ix == 0 or ix % 3 == 0: # Ha a listaindex 0 vagy 3-mal osztható, akkor csak lépésszám kijelzés legyen t_moves.insert(END, mov+" ") else: mov_obj = board.push_san(mov) # Megteszi a lépést a konzolon, ebb?l kinyerjük a teljes lépés leírást(honnan-hova: pl. e2e4) mov_str = str(mov_obj) # A move objectet el?ször sztringgé alakítjuk
    Mutasd a teljes hozzászólást!
  • Most látom, hogy arra a jelenségre panaszkodtál a timerrel kapcsolatban, hogy az csak 1 lépést játszik le. 

    Erre emelném ki a RepeatTimer class-t a "kódomból".

    Ahogy néztem Pythonban "alapból"  nincs olyan timer ami N-szer vagy T időközönként a végtelenségig futna, így a timer-es hívásod csupán 1 másodperc múlva fogja meghívni a beadott függvényt. Egyszer.

    Forrás:
    Python threading.timer - repeat function every 'n' seconds
    Mutasd a teljes hozzászólást!
  • Eddig a sima Timer-t sem használtam soha, bár a létezéséről tudtam. A folyamatos lejátszáshoz lehet, hogy ez a RepeatTimer jó lesz néhány átalakítás után. Köszi, próbálkozom vele.
    Mutasd a teljes hozzászólást!
  • Pluszban ez is lehet a gond, de az is amit írtam. Hiába indult el a timer (tegyük fel, hogy jól) akkor is mindig ugyanazt a lépést ismétli, mert a változó nem növekszik. A timer 1 indítása előtt növelte 1x, majd utána nem változik.

    Szóval nincs messze a megoldás, csak vacakolni kell a Phyton Timer() objektumával is...

    Ha egyszer sikerül, akkor már a többi pofon egyszerű lesz.
    Mutasd a teljes hozzászólást!
  • A lépésenkénti lejátszáshoz jó így, ahogy leírtam. Hiszen a > gomb megnyomása mindig meghívja a forward() függvényt, amelyben még a t timer indítása előtt növelem az ix változót eggyel. Így amikor a timer meghívja a play_pgn() függvényt, ott már ix értéke az előzőnél eggyel nagyobb lesz. A gond továbbra is a folyamatos lejátszással van, illetve annal megállításával és újraindításával. Hogy ez a RepeatTimer-rel vagy ciklussal oldható-e meg, ezt kellene kisilabizálni.
    Mutasd a teljes hozzászólást!
  • Lehet, mivel én sem vagyok ismert a pythonban így nem merültem bele Tibor konkrét kódjába, ezért nem is neked válaszoltam, csak kiegészítésként tettem hozzá.



    Amúgy igen, átfutva a kódot gyanús, hogy a függvények sorrendje és funkciója még nem állt össze rendesen.
    Mutasd a teljes hozzászólást!
  • Nézd, megoldható. Az alap amin elindultál jó. A gond nem itt lesz. Elsőnek old meg, hogy automatikusan mennyen. Ez a timer gond, és az amit mondtam. Utána lehet ráaggatni a plusz dolgokat. Azt, hogy automatikus vagy sem azokat egy plusz logikai változóval kell majd megoldani. Azaz a timer megy, de NEM kell automatikusan akkor ez a rutin meghívódik a timer által, de ki is ugrasztjuk belőle a legelején, mert ez a logikai változó false.  Ezzel már el is lehet dönteni, hogy automatikusan kell futnia vagy sem. A lényeg, hogy MINDEN 2 másodpercben meghívódik majd ez a rutin a timer által, de NEM minden esetben hajtja végre ami benne van. Amíg a programod fut, menni fog a timer, de csak meghatározott esetekben hajtja végre ami benne van.

    Ennél nem tudom jobban megfogalmazni.
    Mutasd a teljes hozzászólást!
  • Sziasztok! Végre sikerült mindent megoldanom a folyamatos és lépésenkénti lejátszással kapcsolatban Timer-ek és RepeatTimer segítségével. A lépéskijelző törlése is megy visszalépésnél és a helyes újraírás előrelépésnél.
    Zizikus, Denzelii, TeddySnow, köszönöm szépen mindhármotoknak a segítséget, a jó ötleteket, példákat, linkeket, türelemet.
    A megoldásban való segítségért legszívesebben mindhármotoknak odaítélném a szakértői pontokat, mert mindegyikőtök hozzájárult valamivel a helyes meoldáshoz. Csak nem tudom, hogy erre van-e lehetőség. Vagy a pontokat meg lehet-e osztani több ember között?
    Mutasd a teljes hozzászólást!
  • Sajnos nincs megosztás, add az első segítőnek :)

    Annyi még, hogy ezt úgy szoktuk megoldani, hogy a play_pgn() visszarakja saját magát a timerbe, hogy újra lefusson, úgy lesz belőle ciklus. A repeatet én sem ismertem, több nyelven nincs ilyen szerintem.
    Mutasd a teljes hozzászólást!
  • Köszi a választ! Ha nincs pontmegosztásra lehetőség, akkor én zizikusnak szeretném adni a szakértői pontokat, mindamellett, hogy mindegyikőtök segítsége, ötlete, meglátása fontos és hasznos volt. De visszanézve a válaszokat, zizikus szólt hozzá legtöbbször, próbált lépésről lépésre rávezetni a helyes megoldásra és ő vetette fel először a Timer használatának gondolatát is, amivel végül sikerült megoldanom ezt a problémát. De mégegyszer mondom, szívem szerint mindhármotoknak adnám a pontokat.
    Köszönöm mégegyszer mindenkinek!
    Mutasd a teljes hozzászólást!
abcd