Form.showmodal

Ragazzi, ho un altro problemino. Nell’esecuzione di una stampa o nel caricamento di dati ho utilizzato un form.show con la scritta “elaborazione in corso”, perch in Windows non compare la rotella dell’esecutivo e in corso come con Mac.Ora ovviamente la form si chiude se clicco da un’altra parte e se la faccio modale non elabora. Come posso risolvere.
Grazie Umberto

Fai in modo che il lancio della stampa, o il caricamento avvenga da dentro la modale, in questo modo puoi anche indicare alla modale quando chiudersi

Grazie Antonio,
avevo pensato di fare cos ma ho 10 stampe devo poi fare una modale per ognuna di esse? Ho pensato anche in alternativa di usare una progresswheel, ma ho visto degli esempi e sinceramente non ho capito come utilizzarla. Ho trovato sul forum inglese uno che ha realizzato una progresswheel pi grande (ovviamente disegnato da lui) con un timer, ma non riesco a farlo scattare nella mia form mi apple sempre frizzata, non ruota. Come devo fare?
Grazie

Non mi sono spiegato.
Il metodo più semplice è creare una modal che eseguirà il tuo codice. Ovviamente una classe non una per ogni stampa…

Per non bloccare il programma vediamo una versione più lunga a descrivere, ma molto più efficiente.

Crei una finestra (diciamo di tipo sheet, così su macOS sarà attaccata alla finestra e su win sarà una modal) chiamiamola wMetodoLungo
dentro ci metti la progresswheel, on un canvas in cui animerai qualcosa se vuoi, e una label per dire cosa sta facendo (tipo “sto stampando questo…, sto stampando quello…”)

Crei una sottoclasse di Thread. (Chiamiamola ThAutomatico)
A questa aggiungi un delegate codiceDaEseguire (pubblico) senza parametri
Aggiungi una proprietà cb (pubblica) as codiceDaEseguire

Un delegate è la firma di un metodo. per cui puoi assegnare alla proprietà cb qualsiasi metodo che non accetti parametri.

Aggiungi un evento hoTerminato (per poter capire quando il thread è finito)
Aggiungi un metodo hoFinito che semplicemente attiva l’evento:

raiseEvent hoTerminato

Implementi il metodo Run con:

if cb<>nil then cb.invoke Xojo.Core.Timer.callLater(10, weakAddressOf HoFinito)

In pratica se esiste un metodo assegnato a cb questo viene eseguito nel thread, poi il tread richiama, fuori dal suo codice in modo da non bloccarsi, il metodo HoFinito che scatena l’evento HoTerminato.

Nella finestra che hai creato aggiungi questa classe (ThAutomatico1) e implementi l’unico evento presente: hoTerminato
self.close

In pratica alla termine dell’esecuzione fai chiudere in automatico la finestra.

Aggiungi un metodo Constructor(messaggio as text, codice as ThAutomatico.codiceDaEseguire)

thAutomatico1.cb = codice super.Constructor Label1.text = messaggio

In pratica assegni al cb del thread il metodo presente in codice e dopo aver chiamato il super.constructor, che viene inserito automaticamente, assegni alla label il cosa stai facendo.

Ora per lanciare la tua stampa (o il tuo metodo molto lungo) fai:
dim w as new wMetodoLungo(“Cosa sto facendo…”, weakAddressOf ilMioMetodoLungo)
w.showModalWithin self

Dove “Cosa sto facendo…” è il messaggio da far apparire all’utente e ilMioMetodoLungo è il metodo che vuoi eseguire.

In questo modo non blocchi l’interfaccia, la progress wheel viene eseguita animata, ma essendo una modal l’utente non può interagire cambiando i parametri.

Ricordati però che essendo eseguito in un thread il tuo metodo non può in nessun modo interagire con l’interfaccia, per cui raccogli prima tutti i dati.

Il tutto è complicato a spiegare, ma molto semplice da eseguire.

Ciao Antonio,
ho seguito, spero, le tue istruzioni, ho inserito nella mia form, quando premo il pulsante, l’istruzione
dim w as new wMetodoLungo("Eseguo report budget ", weakAddressOf esegui)
w.showModalWithin self
“esegui” il mio metodo dove preparo tutti i dati ed eseguo le stampe. Ma praticamente il metodo non viene richiamato ( ho messo un break al suo interno) e la form wMetodoLungo resta frizzata. Forse ho sbagliato a creare il Constructor , ho fatto un metodo sotto la finestra wMetodoLungo chiamandolo Constructor e passando i parametri messaggio as text, codice as ThAutomatico.codiceDaEseguire, poi nel metodo il codice
thAutomatico1.cb = codice
super.Constructor
Label1.text = messaggio
Ho fatto bene? Dove ho sbagliato?
Grazie

Quando crei il constructor dovresti trovare gi scritto super.Constructor, cos come dovresti trovare il nome del metodo nella lista dei nomi possibili per i metodi (stai facendo un override)

Nella finestra che si frizza vedi la label con il testo della label? La progress wheel attiva?
Questo per vedere che tutto sia settato correttamente

Manca il comando di avvio.
Ancora una volta usiamo un timer one shot (tipo quello del run)
Quindi nuovo metodo avvia

thAutomatico1.run

ma per lanciarlo aggiungi al termine del constructor

xojo.Core.Timer.CallLater(10, WeakAddressOf avvia)

Con questo metodo hai una finestra modale, che quindi impedisce ulteriori interazioni, ma che non blocca il programma in quanto il suo codice viene eseguito in un thread. L’uso dei delegate ti permette di rendere il tutto indipendente dal codice (che appunto in un delegate che passi come parametro). Puoi estendere la cosa per avere anche una visualizzazione del progresso del processo.

Tieni conto che usi thread per cui nessuna interazione con l’interfaccia possibile nel codice che stai eseguendo.

Ho aggiunto il metodo avvia nella finestra wMetodoLungo e nel constructor il comando che mi hai detto, ora il metodo esegui parte regolarmente ma fa in crash in questa istruzione

if ckbriepilogo.State = Checkbox.CheckedStates.Checked then

ho provato ad eliminarlo ma va in errore in quello successivo :

leggidatimese(ComboAnno.RowTag(ComboAnno.ListIndex),ComboMese.RowTag(ComboMese.ListIndex),ComboFiliale.RowTag(ComboFiliale.ListIndex),ComboVersione.RowTag(ComboVersione.ListIndex),"budget","G")

Ho pensato che il metodo forse non pu leggere i dati provenienti dalla form possibile? Se cos posso passarli direttamente al metodo esegui in questa istruzione dim w as new wMetodoLungo("Eseguo report budget ", weakAddressOf esegui)?

No

Come ti ho detto il thread non pu accedere ad elementi dell’interfaccia altrimenti vedi gli errori che stai vedendo.

Il metodo migliore raccogliere le informazioni prima e metterle in propriet o in un oggetto apposito PRIMA di avviare il thread/modal poi nel codice che viene eseguito nel thread accedi solo a queste propriet o propriet di una oggetto.

Mi spiego:
Sembra un approccio “strano” ma ha i suoi notevoli vantaggi, ad esempio puoi salvare i parametri (ad esempio se li metti in un oggetto puoi in qualche modo serializzarlo per utilizzi successivi), rendi il codice principale indipendente dall’interfaccia (che puoi quindi inseguito modificare cambiando solo come la nuova interfaccia interagisce con l’oggetto che ne raccoglie i valori)

Quando possibile, o imposto, meglio separare il pi possibile il codice dall’interfaccia per renderlo riutilizzabile (ad esempio se vedi la modale indipendente dal codice, la puoi usare per qualsiasi processo)

D’altronde il vantaggio di questo approccio che il codice risiede nella finestra da cui sei partito, per cui creare un oggetto qui che magari poi elimini al termine (dopo il showModal) senza quindi lasciare cose in giro.

Ho raccolto le informazioni come mi hai detto, finalmente il metodo viene eseguito perfettamente e con la progresswhell tutto funziona, per piccola volevo utilizzare una canvas con una progresswheel disegnata. Ho trovato un esempio ma non riesco a farla girare con il tuo esempio. L’esempio usa un customtimer con l’istruzione

if frmwindows.wheelos1.Enabled = True then frmwindows.wheelos1.invalidate end if nell’action

per una canvas animata, basta che aggiungi un timer alla finestra modale che ogni tot (non esagerare con la frequenza) aggiorna l’immagine (invalidate)
Poi sar il codice del paint del canvas a decidere l’animazione.

Lo puoi far partire nell’open direttamente (magari solo sotto win)

Come vedi il metodo espandibile (puoi anche aggiungere una funzione da richiamare per gli aggiornamenti di una progress bar).
Una volta impostato non devi fare molto e il codice alla fine ti risulta molto pi pulito.

Grazie infinite perfetto, un’ultima domanda , se voglio utilizzarlo per il caricamento di un file devo passare il metodo che legge il file e siccome apro il file con un open dialog lo passo come un parametro indipendente dall’interfaccia come mi hai detto sopra?

Per il caricamento di un file (solo se lo interpreti per cui occorre un certo tempo)
Apri il file con la dialog, poi quando la chiudi memorizzi il riferimento al file da aprire (potrebbe essere nil perch l’utente ha annullato) poi lancia la routine di “interpretazione” con la stessa tecnica

Tutto ok anche con il file, ma ho scoperto che se durante il caricamento vado ad esempio su di un punto fuori dalla finestra modale e clicco scompare tutto anche se resta in esecuzione il programma, non modale? Forse ho sbagliato qualcosa?
Inoltre ho un problema all’interno del file posso trovare errori da segnalare all’utente, prima utilizzava il msgbox ma con questo sistema va in crash tutto come posso risolvere?
Grazie

Come ho detto puoi aggiungere altre funzionalit per mostrare il progresso (un timer nella finestra che legge dei valori e che viene attivato e spento all’avvio del thread, fai attenzione a chi possiede code ovvero alle regole di scopo: devi rendere il tutto indipendente da dove si trova il codice che utilizzi)
Lo stesso per mostrare gli errori, in questo caso interrompi il codice del tread facendo mostrare l’errore all’utente

La msgbox un elemento della UI per cui non puoi lanciarla dal codice del thread, ma nel codice puoi impostare una variabile di errore e far terminare il thread, all’uscita se impostata la condizione di errore la mostri, e fai chiudere la finestra dopo un tempo maggiore e/o mostri un pulsante per la chiusura

Grazie infinite, tutto risolto e perfettamente funzionante.

Ciao Antonio,
scusami pensavo tutto risolto, ma va benissimo se giro sotto mac, appena lancio il programmo sotto windows, parte la mia progresswheel animata , esegue l’elaborazione ,mostra la stampa ma rimane tutto bloccato come se fosse in modale.
Per le stampe uso bkeeneyShort.
La mia istruzione [code]dim w as new wMetodoLungo(“Eseguo Stampa Assenze”, weakAddressOf Elabora)
w.ShowModalWithin self

dim f as FolderItem = GetFolderItem(“AssenzeReport.shorts_report”)
if f = nil then return

dim s as new winRPTViewer
s.display(f)[/code]
Grazie