Newsgroup: it.comp.os.linux.development
Titolo: Aggiornamento testo con TextView di Gtkmm

Salve a tutti,
il mio programma (in c++) ha 2 classi: Gui (che gestisce la parte
grafica usando gtkmm) e Core (il "motore", è un thread della libreria
commoncpp). Più o meno sono così (tralascio le cose non "importanti"
per il mio problema):
-------
class Gui: public Gtk::Window {
public:
  Gui() {
     B_Log = Gtk::TextBuffer::create();
     Log.set_buffer(B_Log);
  };
  Core* core;
  Gtk::TextView Log; 
  Glib::RefPtr<Gtk::TextBuffer> B_Log;
 
  virtual void avvia() {
     core = new Core(this);
     core->start();
  };
  virtual void append(const string& s) {
     B_Log->insert_at_cursor(s + "\n");
  };
};
-------
class Core: public Thread {
public:
  Core(Gui* _gui_): gui(_gui_) {};
  Gui* gui;
  void run() {
  // fa qualcosa ed ogni tanto aggiungi una riga
    gui->append("Testo da inserire in Log");
  };
};
-------
Il tutto funziona bene tranne per un aspetto: il TextView "Log" non
viene "aggiornato" quando effettivamente la classe Core esegue un
gui->append() bensì viene aggiornato quando passo sopra con il
mouse alla TextView stessa. La cosa più brutta è che non ho la più
pallida idea del perchè si comporti così ;)
Avete qualche suggerimento?
Un'altra cosa: per la classe Gtk::Button sempre di gtkmm non ho trovato
un metodo che mi "disabilita" il bottone: visto che il mio programma è
di tipo "avvia"->"ferma" sarebbe necessario disabilitare il bottone
"avvia" quando il core sta già "girando" mentre sarebbe necessario
disabilitare il bottone "ferma" quando il core non sta "girando". In
pratica non ho trovato un metodo simile al JButton.setEnabled(boolean)
su Java.
Consigli?
Grazie e ciao
InSa
-- 
++ Powered by Sid ++

 di tipo "avvia"->"ferma" sarebbe necessario disabilitare il bottone
Devi usare il metodo generico della classe Widget, se hanno mantenuto la 
sintassi simile a quella C e`:
Gtk::Widget::set_sensitive(bool val)
Bye,
  Gabry

 Gtk::Widget::set_sensitive(bool val)
Funziona! :)
(Mi sto sempre di più convincendo che la documentazione di gtkmm è
""scarsina"")


 ""scarsina"")
Devi "integrarla" con le API per il C.
Ciao,
 Emmanuele.

 Devi "integrarla" con le API per il C.
Queste, giusto?
http://developer.gnome.org/doc/API/2.0/gtk/index.html
Il Makefile è questo (anche se non dovrebbe centrare nulla):
--------------
comp=g++
target=primi
MYSQLCOMP=-I/usr/include/mysql -I../lib
MYSQLLINK=-lmysqlclient -lmysqlpp
GMPFLAGS=-lgmpxx -lgmp
GTKMMCOMP=`pkg-config gtkmm-2.4 --cflags`
GTKMMLINK=`pkg-config gtkmm-2.4 --libs`
CCCOMP=-I/usr/include/cc++2
CCLINK=-lccgnu2
all: main.o Core.o Gui.o
	$(comp) main.o Core.o Gui.o -Wall -o $(target) $(GTKMMLINK)
$(MYSQLLINK) $(GMPFLAGS) $(CCLINK)
main.o: main.cpp
	$(comp) -c main.cpp -Wall $(MYSQLCOMP) $(GTKMMCOMP) $(CCCOMP)
Core.o: Core.cpp Core.h
	$(comp) -c Core.cpp -Wall $(MYSQLCOMP) $(GTKMMCOMP) $(CCCOMP)
Gui.o: Gui.cpp Gui.h
	$(comp) -c Gui.cpp -Wall $(MYSQLCOMP) $(GTKMMCOMP) $(CCCOMP)
clean:
	rm -rf *o $(target)
--------------


 
http://developer.gnome.org/doc/API/2.0/gtk/index.html
Yep.
Anche:
 
http://developer.gnome.org/doc/API/2.0/glib/index.html
Specie nella sezione del Main Loop.
Ciao,
 Emmanuele.

 nel 90% dei casi *non ce n'e` bisogno*.
Beh, se nessuno mi spiega mai come farlo senza thread io non so come
fare (visto che gtkmm l'ho presa in mano 2 settimane fa :)
Piccola riflessione:
da quello che ho capito dopo aver chiamato gtk_idle_add() io mi ritrovo
ad avere 2 "cicli" nel main loop: uno per la grafica ed uno per il
loop. Com'è il "dispatcher"? Non rischio di "perdere" troppo in
prestazioni delegando a Gtk la scelta di eseguire il mio loop o la
grafica?
Altrimenti per gtkmm ho visto questo:
http://libsigcx.sourceforge.net/docs/sigcx_starting.html
il secondo esempio "A GUI Example". Utilizza i thread ma se è l'unica
soluzione (per i motivi sopra esposti) sono costretto a fare così.
Ovviamente non conoscendo Gtk, gtkmm e soci sono pronto ad essere
"smentito" :)


 grafica?
No, il ciclo e` sempre lo stesso. Se non ci sono eventi, viene eseguita
la tua funzione di idle; se fosse un timeout, verrebbe eseguita a
intervalli regolari (ovviamente, se non usi un sistema operativo real
time, questa garanzia non e` del tutto presente).
Quando si lavora con sistemi mono-thread, l'hint e` di spezzare
l'elaborazione in vari stati, e di lavorare su uno stato per volta.
Ciao,
 Emmanuele.

 main loop delle GTK. Prova a chiedere sulla mailing list di sviluppo.
Si, e ora che ho più o meno capito come si fa con gtk+ cercando tra
la documentazione ho trovato anche come si fa per gtkmm.
Per i posteri:
// In Core.h
sigc::connection my_idle;
// In Core.cpp
void Core::Start() {
  // prepara i dati
  my_idle = Glib::signal_idle().connect(sigc::mem_fun(*this,
&Core::loop));
};
void Core::Stop() {
  my_idle.disconnect();
  // salva i dati
};
int Core::loop() {
  // fa quello che vuoi _senza_ while
  return true; // se ritorno 0 esegue solo una volta
};
Grazie dell'aiuto ma soprattutto della pazienza :)
Un'ultima cosa: ho una TextView dentro una ScrolledWindow. Come gli
spiego che se il testo visibile deve sempre essere l'ultimo immesso?
(In pratica "scrollare" verso il basso?)


 (In pratica "scrollare" verso il basso?)
Devi creare un TextMark e impostargli la giusta "gravita`".
In C:
  GtkTextIter iter;
  GtkTextMark *end_mark;
  GtkTextBuffer *buffer;
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
  gtk_text_buffer_get_end_iter (buffer, &iter);
  end_mark = gtk_text_buffer_create_mark (buffer, "end", &iter, FALSE);
A questo punto, dici al TextView di scrollare sempre al punto segnato
dal TextMark ogniqualvolta venga inserito del testo, usando il segnale
"insert-text" del TextBuffer:
  g_signal_connect (buffer, "insert-text",
                    G_CALLBACK (buffer_insert_text_cb),
		    end_mark);
Nel callback usi il metodo gtk_text_view_scroll_to_mark() con il
puntatore all'oggetto end_mark.
Ciao,
 Emmanuele.

 Devi creare un TextMark e impostargli la giusta "gravita`".
Perfetto, è bastato aggiungere _una_ riga:
// Aggiunge la stringa s al Log
void Gui::append(const string& s) {
  B_Log->insert(B_Log->end(), s + "\n"); // inserisci il testo
  Log.scroll_to(B_Log->create_mark(B_Log->end())); // scrolla
};
Log è la TextView, e B_Log è il suo TextBuffer associato.
Grazie ancora e ciao.
InSa
Ps: esatto, non ho più domande (per ora ;)
