PDA

View Full Version : [Visual C++] Far comparire una finestra da applicazione console è possibile?


DomusP45
07-02-2014, 09:11
Salve a tutti,
sto lavorando ad un progetto visual C++ 2010, a 64bit in modalità console di windows, insomma modalità testo in prompt dei comandi.

Sto usando OpenGL e OpenCV per elaborare delle immagini, ma mi servirebbe far comparire un bottone nella finestra di OpenGL per chiudere tutta l'elaborazione (assocerei il button_click ad una variabile booleana per fermare un ciclo).

Il problema è che OpenGL non ha una UI nativamente supportata e tutte le librerie che ho trovato in giro (compresa PUI che nel mio caso andava benissimo) sono solo x86 e quando vado a compilare, mi danno errori visto che il progetto è a 64bit. E quindi addio alle librerie gui includibili.

Pertanto avevo pensato una cosa: ma posso fare apparire dal mio progetto console, oltre alla finestra opengl, una finestrella windows con un bottone "STOP" così da interrompere il tutto da lì?

Visto che la mia non è un'applicazione winform, questa cosa è possibile farla senza dover cambiare tutto il progetto?

Come si fa?
Altrimenti non so più dove sbattere la testa...:muro: :muro:

Grazie anticipatamente a tutti.

Daniels118
07-02-2014, 09:47
Non so dirti nello specifico di VC++, ma se segui il metodo "classico" per creare una finestra non dovresti avere problemi. Ti conviene inserire il codice in una funzione separata e richiamarla con CreateThread, così puoi gestire i messaggi del sistema senza intaccare la tua elaborazione.

DomusP45
07-02-2014, 09:51
Non so dirti nello specifico di VC++, ma se segui il metodo "classico" per creare una finestra non dovresti avere problemi. Ti conviene inserire il codice in una funzione separata e richiamarla con CreateThread, così puoi gestire i messaggi del sistema senza intaccare la tua elaborazione.

L'idea sarebbe quella...ma come la creo una finestra in modo classico, se sono in modalità console? Mi sta bene farlo in un thread...ma non so come si crea un finestra in modo classico da un'applicazione non winform..

Daniels118
07-02-2014, 11:01
Crea un progetto winform e copia il sample che viene generato in automatico all'interno del tuo progetto, rinomina la funzione winmain, elimina i parametri (non ti serviranno) e richiamala in un nuovo thread.

DomusP45
07-02-2014, 11:14
Crea un progetto winform e copia il sample che viene generato in automatico all'interno del tuo progetto, rinomina la funzione winmain, elimina i parametri (non ti serviranno) e richiamala in un nuovo thread.

Grazie...vedrò di fare così allora...ti faccio sapere.

sottovento
07-02-2014, 13:29
QT rilascia il codice sorgente, che quindi puoi ricompilare a 64 bits sotto VS2010.
Ovviamente qt e' richiamabile da console project, ed hai una GUI portabile su sistemi diversi.

In alternativa, potresti farti un progetto GUI a parte (con qualsiasi bitness), il quale comunica con la parte console-based mediante TCP/IP socket (o qualsiasi altro modo standard preferisci)

DomusP45
10-02-2014, 10:37
Ragazzi,
considerando che a me il bottone servirebbe in pratica solo per interrompere tutto il ciclo di elaborazione (passando da false a true una variabile booleana controllata da vari cicli while) su internet trovo che si possa fare anche direttamente da console, associando il cambio variabile ad un evento da tastiera.

Questa cosa come si fa? Perchè in tal caso, eviterei proprio di introdurre ulteriori librerie ad appesantire il programma e farei questa cosa...

Qualcuno sa aiutarmi?

Daniels118
10-02-2014, 10:39
Non puoi semplicemente usare Ctrl+C come si fa da decenni? :D

DomusP45
10-02-2014, 11:02
Non puoi semplicemente usare Ctrl+C come si fa da decenni? :D

Eh, ma come associare a Ctrl+C il fatto di cambiare una variabile booleana?

Daniels118
10-02-2014, 13:06
Non ce ne è bisogno, la combinazione Ctrl+C sia su Windows che su Unix fa terminare il programma senza che quest'ultimo debba implementare nulla.

DomusP45
10-02-2014, 13:17
Non ce ne è bisogno, la combinazione Ctrl+C sia su Windows che su Unix fa terminare il programma senza che quest'ultimo debba implementare nulla.

Ok, però a me serve che quando questa cosa accade, i thread che ho lanciato terminano a cascata in funzione di una variabile booleana, e mi diano l'output dei loro "lavori" non che si chiuda completamente il programma.

Daniels118
10-02-2014, 13:57
In questo caso devi implementare un handler e registrarlo al sistema operativo, vedi qui:
http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
http://www.cplusplus.com/forum/beginner/1501/#msg5410

WarDuck
11-02-2014, 13:52
Ok, però a me serve che quando questa cosa accade, i thread che ho lanciato terminano a cascata in funzione di una variabile booleana, e mi diano l'output dei loro "lavori" non che si chiuda completamente il programma.

Se sei su Windows prova questa:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx

DomusP45
11-02-2014, 16:43
In questo caso devi implementare un handler e registrarlo al sistema operativo, vedi qui:
http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
http://www.cplusplus.com/forum/beginner/1501/#msg5410

Se sei su Windows prova questa:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx

Grazie...domani in mattinata provo e vi faccio sapere...Grazie davvero.

DomusP45
12-02-2014, 12:31
Ragazzi, aggiungo un post giusto per far in modo che vi venga notificato:

Ho introdotto queste modifiche nel mio codice:

#include <signal.h>

bool forever = false;

//questa è la funzione di handler per la combinazione
void sighandler(int sig)
{
cout<<endl<<"Segnale n. "<<sig<<" rilevato. Terminazione programma."<<endl;
//cambia il valore della variabile booleana
forever = true;
}

...
qui ci sono le altre funzioni void, dei thread
...



int main (..)
{

--altro codice---

signal(SIGABRT, &sighandler);
signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler);

while (!forever)
{
.....
}


WaitForSingleObject(handle0,INFINITE);
WaitForSingleObject(handle1,INFINITE);
WaitForSingleObject(handle2,INFINITE);
WaitForSingleObject(handle3,INFINITE);
WaitForSingleObject(handle4,INFINITE);

....output del programma...

return 0;
}

ovviamente lo stesso while

while (!forever)
{
.....
}


c'è in tutti i thread, che infatti vengono avviati tutti.

Il problema è che premendo "CTRL+C" il segnale viene rilevato (infatti l'output viene fuori) ma i thread non terminano. E nemmeno la parte del main vincolata a quella booleana.

Sapete darmi una mano per ottenere questo risultato? Che quando quella variabile "globale" diviene true, funzioni come stop?

Daniels118
12-02-2014, 13:17
Devi dichiarare la variabile forever con la parola chiave "volatile" per impedire al compilatore di fare alcune ottimizzazioni che non vanno proprio d'accordo con il multithreading.

DomusP45
12-02-2014, 14:11
Devi dichiarare la variabile forever con la parola chiave "volatile" per impedire al compilatore di fare alcune ottimizzazioni che non vanno proprio d'accordo con il multithreading.

questa cosa non la sapevo. E come si fa?

Daniels118
12-02-2014, 14:18
Così: :angel:

UAHAHAH scusa ma non ho potuto resistere :D
Si scrive così:
volatile bool forever = false;

DomusP45
12-02-2014, 14:24
Così: :angel:

UAHAHAH scusa ma non ho potuto resistere :D
Si scrive così:
volatile bool forever = false;

Gentilissimo...purtroppo anche se "volatile" non va. I cicli non terminano, come se appunto la variabile non cambiasse...:muro:

come posso fare?

Daniels118
13-02-2014, 08:17
E' un po' difficile capire dove sia il problema senza vedere l'intero programma, comunque puoi fare una semplice prova, inserendo delle istruzioni di stampa:
cout<<"Inizio loop\n";
while (!forever)
{
.....
cout<<(forever ? "true\n" : "false\n");
}
cout<<"Fine loop\n";
Il ciclo stamperà continuamente "false", premendo Ctrl+C dovrebbe stampare "Segnale rilevato... Fine loop".

DomusP45
13-02-2014, 09:05
E' un po' difficile capire dove sia il problema senza vedere l'intero programma, comunque puoi fare una semplice prova, inserendo delle istruzioni di stampa:
cout<<"Inizio loop\n";
while (!forever)
{
.....
cout<<(forever ? "true\n" : "false\n");
}
cout<<"Fine loop\n";
Il ciclo stamperà continuamente "false", premendo Ctrl+C dovrebbe stampare "Segnale rilevato... Fine loop".

infatti a me il segnale viene rilevato, ma i threads che usano lo stesso while non terminano...insomma, come se la variabile non cambiasse, quando invece cambia eccome (visto che nel main il ciclo va avanti)

Questa sarà una cacchiata, ma intanto mi sono bloccato su questa cosa...:muro:

Daniels118
13-02-2014, 09:36
Come fai a dire che nel main va avanti e negli thread altri no? Hai inserito delle cout? Senza vedere cosa hai scritto nei thread si può anche immaginare che all'interno dei cicli tu abbia messo delle istruzioni che non terminano.

DomusP45
13-02-2014, 09:56
Come fai a dire che nel main va avanti e negli thread altri no? Hai inserito delle cout? Senza vedere cosa hai scritto nei thread si può anche immaginare che all'interno dei cicli tu abbia messo delle istruzioni che non terminano.

Si ci sono gli output. Il codice è quello che ho postato nel post precedente...nel main e nei thread c'è lo stesso ciclo che si occupa di varie cose, ma tutti i cicli sono vincolati a quella variabile. Visto che c'è la funzione legata a quell'evento, quando lo premo, ottengo l'output della funzione legata al CTRL+C...

Daniels118
13-02-2014, 09:59
Ho capito che ottieni l'output della callback, ma all'interno dei cicli hai messo un'istruzione di output per verificare che effettivamente non terminano?
Cioè se per assurdo tu avessi scritto
while (!forever) {
while (true);
}
sarebbe del tutto normale che i thread non terminano. Senza vedere il tuo programma è impossibile capire cosa c'è che non va, quindi o fai un debug serio, oppure scrivi l'intero programma.

DomusP45
13-02-2014, 12:02
Ho capito che ottieni l'output della callback, ma all'interno dei cicli hai messo un'istruzione di output per verificare che effettivamente non terminano?
Cioè se per assurdo tu avessi scritto
while (!forever) {
while (true);
}
sarebbe del tutto normale che i thread non terminano. Senza vedere il tuo programma è impossibile capire cosa c'è che non va, quindi o fai un debug serio, oppure scrivi l'intero programma.

Per farti capire come sono fatte sia le funzioni di thread che il main, eccoti il codice. I cicli sono ora solo nei thread, ma nonostante venga intercettato il ctrl+c, i thread non si fermano.

Ecco qua:




variabili varie, struct ed altra roba che non c'entra con i thread


/* PARTE FUNZIONI PER THREAD */


volatile bool forever = false;

//funzione di popolamento
void puts (void* param){
Args* arg = (Args*)param;
int im=0;
while(!forever){
arg->out->push(IMG[im]);
im++;
}
cout<<endl<<"Thread (PUSH) terminato con "<<im<<" elaborazioni."<<endl;
_endthread();
}

//funzione grey
void grey (void *param){
Mat temp1;
int add = 0;
Args* arg = (Args*)param;
while(!forever){
temp1=arg->in.try_pop();
cvtColor(temp1,temp1,CV_BGR2GRAY);
arg->out->push(temp1);
add++;
}
//fine
cout<<endl<<"Thread (GREY) terminato con "<<add<<" elaborazioni."<<endl;
_endthread();
}

//funzione sogliatura
void soglia(void *param){
Mat temp2;
int add=0;
Args* arg = (Args*)param;
while(!forever){
temp2=arg->in.try_pop();
threshold(temp2,temp2,0,255,THRESH_BINARY| THRESH_OTSU); //soglia adattativa
arg->out->push(temp2);
add++;
}
//fine
cout<<endl<<"Thread (SOGLIA) terminato con "<<add<<" elaborazioni."<<endl;
_endthread();
}

//funzione erosione
void finitura(void *param){
Mat temp4;
int add = 0;
Args* arg = (Args*)param;
while(!forever){
temp4=arg->in.try_pop();
dilate(temp4,temp4,Mat());
erode(temp4,temp4,cv::Mat());
arg->out->push(temp4);
add++;
}

//fine
cout<<endl<<"Thread (ERODE) terminato con "<<add<<" elaborazioni."<<endl;
_endthread();
}

//funzione verfi
void verifica(void *param){
int add = 0;

Args* arg = (Args*)param;
//ciclo di elaborazione
while(!forever){
Mat tempA=arg->in.try_pop();
prova.verfii(tempA);
add++;
}

//fine thread
cout<<endl<<"Thread (VERIFICA) terminato con "<<add<<" verifiche aggiuntive."<<endl;
_endthread();
}

//funzione intercetta tastiera
void sighandler(int sig)
{

cout<<endl<<"Segnale terminazione rilevato."<<endl;
forever = true;
}


/* INIZIO DEL MAIN */

int main(int argc, char **argv)
{

//dichiarazione code per i thread
Args dati0,dati1,dati2,dati3,dati4;

//puntatori per il passaggio alla coda successiva
dati0.out=&dati1.in;//da push a grey
dati1.out=&dati2.in;//da grey a soglia
dati2.out=&dati3.in;//da soglia a erode
dati3.out=&dati4.in;//da erode a verifica


//dichiarazione delle handle
HANDLE handle0,handle1,handle2,handle3,handle4;

//dichiarazione dei threads
handle0 = (HANDLE) _beginthread(puts,0,&dati0);
handle1 = (HANDLE) _beginthread(grey,0,&dati1);
handle2 = (HANDLE) _beginthread(soglia,0,&dati2);
handle3 = (HANDLE) _beginthread(finitura,0,&dati3);
handle4 = (HANDLE) _beginthread(verifica,0,&dati4);


//join
signal(SIGABRT, &sighandler);
signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler);

WaitForSingleObject(handle0,INFINITE);
WaitForSingleObject(handle1,INFINITE);
WaitForSingleObject(handle2,INFINITE);
WaitForSingleObject(handle3,INFINITE);
WaitForSingleObject(handle4,INFINITE);


...qui c'è solo la parte dell'output finale...

return 0;
}

I thread vengono eseguiti tutti, e fanno quello che devono restando in attesa di altro input dalle proprie code.
Se aspetto le 9 elaborazioni della verifica (quindi quando nessun thread può fare altro e sono tutti in attesa di input) facendo ctrl+c vedo solo la scritta dell'output della funzione correlata.

Se faccio ctrl+c mentre magari sono alla 4 elaborazione da parte di verifica, l'unico thread che si chiude e visualizza l'output è verifica (probabilmente perchè è l'unico che sta ancora facendo qualcosa, gli altri sono molto più veloci) mi si visualizza solo la scritta di uscita di verfica e quella della funzione di intercettazione tastiera, ma non quelle relative alle uscite degli altri thread. Quindi gli altri thread non si interrompono, infatti il programma non si chiude e devo farlo io col mouse.

Daniels118
13-02-2014, 14:24
Dunque... prima di tutto nel thread "puts" non vedo nessuna condizione sul numero di iterazioni, la variabile "im" potrebbe sforare la dimensione del vettore "IMG" e causare violazioni di accesso.

Secondo, se il metodo try_pop è quello della classe concurrent_queue, non capisco come possa compilare... quindi:

Terzo. Quanto hai scritto, congiuntamente con la logica degli altri thread, mi suggerisce che il metodo try_pop è sincrono, nel senso che se la coda è vuota si blocca. Se così non fosse le variabili "temp" non sarebbero correttamente valorizzate e le successive chiamate andrebbero male. Nell'ottica di un try_pop sincrono, il comportamento è esattamente quello che avevo previsto nel mio precedente post, l'esecuzione si blocca su quel metodo e non raggiunge mai l'istruzione di controllo di uscita dal ciclo.

Riassumendo: il tutto è ancora un po' mistico, cerca di fare chiarezza su questi punti.

DomusP45
13-02-2014, 14:35
Dunque... prima di tutto nel thread "puts" non vedo nessuna condizione sul numero di iterazioni, la variabile "im" potrebbe sforare la dimensione del vettore "IMG" e causare violazioni di accesso.

Secondo, se il metodo try_pop è quello della classe concurrent_queue, non capisco come possa compilare... quindi:

Terzo. Quanto hai scritto, congiuntamente con la logica degli altri thread, mi suggerisce che il metodo try_pop è sincrono, nel senso che se la coda è vuota si blocca. Se così non fosse le variabili "temp" non sarebbero correttamente valorizzate e le successive chiamate andrebbero male. Nell'ottica di un try_pop sincrono, il comportamento è esattamente quello che avevo previsto nel mio precedente post, l'esecuzione si blocca su quel metodo e non raggiunge mai l'istruzione di controllo di uscita dal ciclo.

Riassumendo: il tutto è ancora un po' mistico, cerca di fare chiarezza su questi punti.

Anzitutto grazie mille per la pazienza e la disponibilità. Sei gentilissimo.

1) Il metodo puts ha il suo limite attualmente, ma nello sviluppo futuro il numero di immagini non saranno fisse, quindi l'ho omesso solo per questo motivo, per non vincolare ad un numero fisso la cosa, scusami.

2 e 3) Si, è una coda concorrente e si, è fatta in questo modo:


template<typename T>
class coda_concorr
{
private:
std::queue<T> la_coda;
CRITICAL_SECTION m_crst;
CONDITION_VARIABLE m_nonvuoto;
public:
bool stop;
//costruttore
coda_concorr()
{
InitializeCriticalSection(&m_crst);
InitializeConditionVariable(&m_nonvuoto);
stop = false;
}
//distruttore
~coda_concorr()
{
//delete sezione critica;
DeleteCriticalSection(&m_crst);
}
//funzione di push thread safe
void push(T& data)
{
EnterCriticalSection(&m_crst);
la_coda.push(data);
WakeConditionVariable(&m_nonvuoto);
LeaveCriticalSection(&m_crst);
}
//funzione di pop thread safe
T try_pop()
{
EnterCriticalSection(&m_crst);
//wait
while (la_coda.empty())
SleepConditionVariableCS(&m_nonvuoto, &m_crst, INFINITE);

T popped = la_coda.front();
la_coda.pop();
LeaveCriticalSection(&m_crst);
return popped;
}
bool stopped()
{
stop = true;
return stop;
}
};

Quindi, se è dovuto alla coda, come posso risolvere?

Ancora grazie infinitamente.

Daniels118
13-02-2014, 15:03
Per il punto 1 non c'è bisogno di scuse, ma quanto meno di un if :)

while (la_coda.empty())
SleepConditionVariableCS(&m_nonvuoto, &m_crst, INFINITE);
Eh, lo dicevo che c'era :D

La soluzione più corretta secondo me è rendere il metodo try_pop non bloccante, come fa l'omonimo nella classe concurrent_queue: il valore estratto dalla coda - se c'è - viene assegnato al parametro passato per riferimento, mentre il valore di ritorno indica se l'elemento è stato estratto oppure se la coda era vuota; nel ciclo dove chiami la funzione testi il valore di ritorno e decidi se elaborare "il parametro" o riprovare ad estrarre, magari inserendo una pausa.
Guarda qui per il metodo try_pop:
http://msdn.microsoft.com/en-us/library/ee355368.aspx
E qui per un esempio di Producer-Consumer:
http://blogs.msdn.com/b/nativeconcurrency/archive/2009/11/23/the-concurrent-queue-container-in-vs2010.aspx

Una soluzione forse più efficiente sarebbe quella di aggiungere un metodo alla classe coda_concorr che forzi m_nonvuoto in modo da sbloccare l'esecuzione, ovviamente bisognerebbe mettere diversi controlli sia nel metodo try_pop che nelle routine chiamanti per evitare che venga elaborato qualcosa che non c'è. Questo metodo "magico" andrebbe poi richiamato dal signal handler su ogni coda.

DomusP45
13-02-2014, 15:11
Per il punto 1 non c'è bisogno di scuse, ma quanto meno di un if :)

while (la_coda.empty())
SleepConditionVariableCS(&m_nonvuoto, &m_crst, INFINITE);
Eh, lo dicevo che c'era :D

La soluzione più corretta secondo me è rendere il metodo try_pop non bloccante, come fa l'omonimo nella classe concurrent_queue: il valore estratto dalla coda - se c'è - viene assegnato al parametro passato per riferimento, mentre il valore di ritorno indica se l'elemento è stato estratto oppure se la coda era vuota; nel ciclo dove chiami la funzione testi il valore di ritorno e decidi se elaborare "il parametro" o riprovare ad estrarre, magari inserendo una pausa.
Guarda qui per il metodo try_pop:
http://msdn.microsoft.com/en-us/library/ee355368.aspx
E qui per un esempio di Producer-Consumer:
http://blogs.msdn.com/b/nativeconcurrency/archive/2009/11/23/the-concurrent-queue-container-in-vs2010.aspx

Una soluzione forse più efficiente sarebbe quella di aggiungere un metodo alla classe coda_concorr che forzi m_nonvuoto in modo da sbloccare l'esecuzione, ovviamente bisognerebbe mettere diversi controlli sia nel metodo try_pop che nelle routine chiamanti per evitare che venga elaborato qualcosa che non c'è. Questo metodo "magico" andrebbe poi richiamato dal signal handler su ogni coda.

grazie mille...allora è dovuto a quello...beh, vedrò quello che posso fare. Prima che il PC mi :lamer: