Qsharedpointer-spor

Glatt og responsivt brukergrensesnitt som holder god animasjon selv nar programmet utforer tunge oppgaver. Dette er ikke hyggelig a ha & # 8221; funksjonen, men en viktig og & # 8220; en ma ha & # 8221; funksjon av programmet hvis det er plassert som profesjonelt produsert produkt. Men folgende offisielle guider for a bygge Qt / QML-applikasjoner vil ikke automatisk levere denne funksjonen. Man kan finne noen ideer om hvordan man far det pa http://doc.qt.digia.com/qq/qq27-responsive-guis.html, men det er vanskelig a vedta disse ideene i store programmer & # 8211; for mye manuell arbeid og det skaleres darlig. Gode nyheter om at det er en komplett, enkel og enkel mate a fa onsket funksjon pa. Hopp under kuttet hvis du trenger noen & # 8230;

La oss starte med et program som viser problemet: svn co https: / / github .com / kocherovms / develorium .com / trunk / task_manager_in_qml_apps / 0. Programmet viser to mulige brukstilfeller:

spilleautomat som tar for lang tid a utfore (venstre klikk); kjede pa kortspillhandterer som clue sammen og danne en lang kjorerute (hoyreklikk).

Begge tilfeller gjor ubehagelig UI-fryse (skjermen stopper a spinne).

Forste problemet kan reduseres ved a lage kode for Manager :: onSlowOperation som skal utfores asynkront. Dette kan oppnas ved hjelp av QtConcurrent :: lop med tilhorende venter pa ferdigbehandling i QEventLoop :: exec.

Andre problem kan reduseres ved a hindre kjedet utforelse av spor i en tur. Dette kan oppnas ved hjelp av QCoreApplication :: processEvents innenfor slutten av hver av sporet & # 8211; Dette ville gi UI-tid for animasjon mellom folgelig slotsangrep.

Kode med begge problemene redusert er her svn co https: / / github .com / kocherovms / develorium .com / trunk / task_manager_in_qml_apps / 1.

Begge begrensende tiln rminger har en fallgruv som diskuteres i dette innlegget: de tillater nestet (var av ordre) utforelse av koden din, som kan fore til subtile bugs. Selvfolgelig kan vi noe beskytte oss selv ved a spesifisere QEventLoop :: ExcludeUserInputEvents nar du lager QEventLoop :: exec eller QCoreApplication :: processEvent, men det er ikke nok siden:

dette virker ikke bra pa noen plattformer; Koden din kan legge ut tilpassede hendelser, og dette forhindres ikke av QEventLoop :: ExcludeUserInputEvents.

Sa det vi trenger er en mekanisme som:

Beskytter mot nestet kjoring av kode mens du venter pa mulige asynkrone samtaler; gjor det enkelt a knuse lange kjeder med korte spor.

La oss se hvordan en enhet som heter TASK MANAGER, kan hjelpe her.

2 Synopsis.

Oppgavebehandling er en hierarkisk, endelig statlig maskin som bestar av scenarier (pa overste niva) som inneholder antall oppgaver (pa bunnliva): hvert scenario gjenspeiler en del forretningslogikk som krever antall trinn (oppgaver) som skal utfores.

Scenarier kan danne en kjede, overgang fra nav rende scenario til neste er bare mulig nar det ikke er flere oppgaver som skal utfores i dagens scenario.

Hver oppgave i et scenario er en metode som kan paberopes ved navn. I Qt oppnas dette perfekt ved bruk av spor eller metoder merket med Q_INVOKABLE direktiv.

Oppgaver innenfor et scenarioformet rettet acyklisk graf for utforelse: Det kan v re grener i utforelse avhengig av resultatene av oppgavene. Det betyr at hver oppgave returnerer resultatet av utforelsen, og avhengig av dette resultatoppgavene, kan leder velge forskjellige etterfolgere for denne oppgaven.

Overgang mellom pafolgende oppgaver er hendelsesbasert. Det betyr at etter utforelse av gjeldende oppgaveoppgavebehandling reschedulerer seg selv og returnerer kontroll til en hendelseslokke. Dette gjor det mulig a frigjore brukergrensesnittet i langkjedede scenarier.

Siden overgangen mellom oppgavene er under full kontroll av oppgavebehandling, gjor det veldig enkelt a beskytte programkoden fra nestet utforelse: Oppgavebehandling kan lett oppdage slike tilfeller ved a bruke en slags teller og dermed kunne avbryte et ulovlig forsok pa a starte oppgaveutforelse i midt i en annen oppgave. Dette er tilfellet nar du sender forsinkelse av langsom spor via QtConcurrent :: run og vent pa utforelsen via temporal (lokal) QEventLoop.

3 Implementeringsdetaljer.

Eksempel implementering av oppgavebehandling er her: svn co https: / / github .com / kocherovms / develorium .com / trunk / task_manager_in_qml_apps / 2. La meg vise hvordan det fungerer fra innsiden (filer taskmanager.cpp og taskmanager.h).

Oppgavebehandling er representert av et objekt av TaskManager-klassen som har veldig enkelt grensesnitt:

Forste metode TaskManager :: addScenario lager nytt scenario og legger det til halen av FIFO-ko scenarier_. Senere velger prosjektleder scenarier fra denne koen og utforer dem.

Scenarier modelleres ved hjelp av en scenarieklasse som har folgende grensesnitt:

Hvert scenario har en rotoppgave som automatisk opprettes, og det peker pa en dummyoppgave implementert av TaskManager selv. Root oppgave har flere formal:

Den tjener som et inngangspunkt nar du bygger en kjoringsgraf: Du kan binde neste oppgave senere til denne rotte oppgaven pa en ensartet mate; det peker pa gjeldende oppgave i et scenario.

Andre metoder for Scenario-klassen er selvforklarende.

Oppgaver er modellert av en oppgaveklasse som har folgende grensesnitt:

Hvert objekt av oppgave definerer:

et objekt (Oppgave :: getObject) som en navngitt metode (Oppgave :: getMethodName) ma utfores med gitt argumenter (Oppgave :: getArguments); suksess (Oppgave :: getSuccessSuccessor) og feil (Oppgave :: getFailureSuccessor) etterfolgere. Den forste kalles nar oppgaven fullforer TaskManager :: Suksessstatus, den andre nar oppgaven fullfores med TaskManager :: Feilstatus.

To metoder brukes til a bygge en utforelsesgraf:

Metodeoppgave :: addNextTask lager en ny oppgave og knytter den til denne oppgaven for begge roller: som en suksess og fiasko etterfolgere. Dette brukes nar resultatet av denne oppgaven ikke er av interesse, og kan ignoreres. Second One Method Task :: addNextTaskBranch lager to dummy oppgaver: den forste for suksess og den andre for mislykkede etterfolgere. Senere kan du binde til denne oppgaven ny oppgave via Oppgave :: addNextTask og danner forgrening av koden din, avhengig av resultatene av denne oppgaven. F.eks anser at denne oppgaven er brukt til a opprette en ressurs som deretter forbrukes av de neste oppgavene. I dette tilfellet kalles du Oppgave :: addNextTaskBranch for denne oppgaven og lenkeoppgaver som forbruker opprettet ressurs pa Branch :: successTask-medlem mens Branch :: failureTask-medlem brukes til a koble opp oppgaver som handterer feilssituasjonen.

Den mest interessante delen er hvordan oppgaven blir utfort & # 8211; metode TaskManager :: prosessTask:

Forst av alt kontrollerer TaskManager om det er et forsok pa a utfore nestet kode. For dette NestingLevelGuard er etablert som implementerer RAII over interne counter nestingLevel_. Hvis det skjer at nestingLevel_ overstiger 1, slar TaskManager ut kontrollen til en hendelseslokke.

Deretter velger TaskManager nav rende scenario, og hvis det er noen som velger en rotoppgave. Det er spesiell handtering for dummyoppgaver & # 8211; de blir bare ignorert siden de eksisterer bare for enkel utforelse av grafgrafikk (som et lim mellom virkelige oppgaver). Ikke-dummy (ekte) oppgaver utfores ved bruk av dynamisk metodeinvitasjon via QMetaObject :: invokeMethod:

Det er noe problem med Q_ARG macro & # 8211; det kan bare brukes pa plass, sa jeg burde bruke ubehagelig stil med argumenter som pakker ut med mange repetisjoner.

Feil under metodepakalling (for eksempel metode er frav rende eller returnerer TaskResult :: MetaError) dodelig feil heves & # 8211; Dette er en slags kompileringsfeil, men under runtime.

Hvis metoden for en oppgave ble utfort med et annet resultat enn TaskResult :: MetaError, blir oppgaven fjernet fra scenario og neste rotteoppgave velges basert pa resultat av utfort oppgave: enten vellykket eller mislyktes etterfolger velges.

Hvis det fortsatt er noe a behandle (roddoppgave innen nav rende scenario), omstiller TaskManager seg selv. Dette gjores ved a starte QTimer pa nytt pa folgende mate:

Det mest interessante her er bruken av 0 for et intervall pa nextTaskTimer_ & # 8211; Dette trikset forteller hendelseslokke (som ogsa gir brukergrensesnitt) at det ma ringe TaskManager tilbake (TaskManager :: onNextTaskTimer) nar det ikke er flere ventende hendelser som skal utfores. Hyggelig og enkel.

Hvis det ikke er flere scenarier, stopper TaskManager sine aktiviteter til nytt scenario med oppgaver er opprettet.

Det er alt om hvordan TaskManager fungerer. Det tok bare.

200 linjer med kode, men det letter skriving av QML-applikasjonen sa mye! La oss se hvordan vi kan omskrive var proveapplikasjon ved hjelp av TaskManager.

Omarbeidet soknad som bruker TaskManager er her: svn co https: / / github .com / kocherovms / develorium .com / trunk / task_manager_in_qml_apps / 2. Som et generelt notat ma man revurdere programmeringsmodell som brukes. Na slots vunnet ikke brukes til a utfore noen applikasjonskode, men heller vil de opprette scenarier i TaskManager som er delegert for a gjore alt arbeidet.

La ‘s starte med sakte drift. Alle koden fra denne sporet flyttet den til ny Q_INVOKABLE metodebehandling :: taskSlowOperation:

Slot Manager :: onSlowOperation er omskrivet slik:

Her oppretter vi bare et nytt scenario med den eneste oppgavestyringSlowOperation. Dette omstiller automatisk TaskManager, og den kjorer denne metoden sa snart den mottar kontroll fra en hendelseslokke. Enkelt, er det ikke? Prov det na, og observer at TaskManager effektivt forhindrer nestekodet kjoring mens du venter i en lokal QEventLoop.

La na & # 8217; s bytte til en langkjedet operasjon. Dette krever:

lage spor (dispatcher) som vil skape tilsvarende scenario innenfor TaskManager; konvertere alle eksisterende spor til oppgaver; fjern Qt-tilkoblinger av konverterte spor.

Her ser du hvordan nye dispatcher-spor ser ut:

Selvforklarende og enkel. Tidligere slots ser na ut som:

Prov ny implementering av langkjedet operasjon. Se hvordan brukergrensesnittet holdes jevnt og ikke nestet kodeutforelse!

5. Konklusjon.

Hovedproblemet med SIGNAL / SLOT-konseptet i Qt er at det ikke fungerer godt innenfor komplekse QML-applikasjoner med tunge spor som kan blokkere hovedhendelseslokken og dermed gjore UI-fryse. Det er fordi sporene kjores synkront fra kode som sender signal. Forsok pa a bruke Qt :: QueuedConnection forer til subtile bugs siden forst og fremst transaksjonelle utforelsesstrommen er brutt, slik at andre spor kan utfores mellom.

Dette problemet er lost ved a delegere kodeutforelse til en dedikert enhet & # 8211; oppgavebehandling. Sistnevnte tar seg av a splitte lang kjede av spor og forhindrer nestekodet kjoring. Bruk av oppgavebehandling folger perfekt metoden for asynkron DB-operasjoner i QML, fordi na oppgavebehandling tar kjerne eller ringer oppgavemetode i stedet for hendelseslokke.

Det eneste man ma huske er at soknaden virkelig vil ha nytte av bruk av oppgavebehandling bare nar all programkode blir utfort via denne mekanismen. Ellers far du blandede kodeksjonsregler som er sarbare for subtile bugs.