Gtkmm - Multithreading TextView Kommunikation mit Haupthread; MainLoop Eingriff



  • Hallo alle zusammen!

    Ich habe ein Problem! Wie kann ich aus einem Nebenthread ein Update aller
    Widgets erzielen, bzw. wie kann ich nur ein Widget "in meinem Fall TextView"
    nach jedem reinschreiben in den TextBuffer den Text am Bildschirm sehen (weitere Fehlerbeschreibung siehe weiter unten punkt 2).

    Andere Frage ist wie kann ich in den Hauptthread eingreifen?

    Mein Problem ist nähmlich, das ich die RS232 Schnittstelle in einem neben
    Thread abfrage ob Daten vorhanden sind. Bei vorhanden sein führe ich ein
    signal.emit() aus. Dadurch starte ich einen nächsten Thread. Beim zweiten Thread
    wird der vector ausgelesen und in ein von mir benötigtes Format umgerechnet.

    2. Die Zahlen will ich am TextView ausgeben und das geht nicht richtig. Ich muss ständig mit der Maus in das TextView Fenster hinein klicken damit was erscheint.
    Es geht nicht automatisch.

    Daher dachte ich ich mache ein Variable die ich abfrage ob neue Daten vorhanden
    sind und die errechneten Daten dann im Hauptthread ins TextView reinschreibe.
    Deshalb ist die frage lässt sich in den Hauptthread eingreifen sowie es bei QT auch geht.

    Was ich noch festgestellt habe ist das bei den Signal/Slot Verbindungen bei ausführen des Signals es erst zurückkehrt wenn der Slot abgearbeitet wurde.
    Lässt sich dies entkoppeln.

    Bin schon am verzweifeln, bitte daher um Hilfe oder auch für andere Ideen!

    Danke im vorraus!



  • moin,

    also ich habe gestern eine ähnliche Anwendung realisiert. Dabei lasse ich einen Childthread Daten aus einer RS232-Schnittstelle auslesen und den Hauptthread die Daten in einem TextView anzeigen.

    Die Datenverbindung für die beiden Threads realisiere ich über eine Ringbuffer Klasse, in der der eine Thread schreibt und der andere liest.

    Das ist nur mal als andere Idee gemeint, bzw. so wie ich es gemacht habe. Ich hatte leider auch erst Probleme mit der Signal/Slot Technik. Daher sind meine Threads Methoden meiner Window Klasse.

    Mit welchen Befehlen bedienst du denn das TextView? Und wie willst du es mit neuen Daten füttern? (Quellcode) Gibt es Fehlermeldungen?



  • Danke für die antwort, hier kurz die erklärung meines Vorgehens.

    In der unten stehenden Funktion wird mir "read_RS232(receiveBytesNumber)" die Schnittstelle gelesen die Funktion ist ein Thread. anschließend schiebe ich die Werte in ein std::vector "vectorReceive" hinein. Im if Zweig wird abgefragt ob der Zweite Thread der die Daten in den TextView schreibt fertig ist, weil nur dann wieder ein signal ausgelöst wird.

    void Thread_RS232::run_receive()
    {
    	unsigned int receiveBytesNumber = 1;
    
    	while(runReceiveFlag)
    	{
    		if(read_RS232(receiveBytesNumber))
    		{
    			//std::cout << "RUN THREAD OK\n";
    			mutexReceive.lock();
    			for(unsigned int counter = 0; counter < receiveBytesNumber; counter++)				
                    vectorReceive->push_back(get_read_value(counter));
    			mutexReceive.unlock();
    
    			erase_vector_receive();
    
    			if(!statusFlagParentThread)
    			{
    				//std::cout << "Send Signal\n";
    				statusFlagParentThread = true;
    				signal_update_receive_value.emit();
    
    				//std::cout << "After Send Signal\n";
    			}
    		}
    	}
    }
    

    in diesem Constructer initialisiere ich den Buffer und übergebe ihn an Textview

    Communication_Interface::Communication_Interface()
    :column_record(NULL), treeModelColumn_value(NULL), treeModelColumn_name(NULL), listStore_comx(NULL)
    {
    	column_record = new Gtk::TreeModelColumnRecord;
    
    	treeModelColumn_value = new Gtk::TreeModelColumn<unsigned long int>;
    	treeModelColumn_name = new Gtk::TreeModelColumn<Glib::ustring>;
    
    	column_record->add(*treeModelColumn_value);
    	column_record->add(*treeModelColumn_name);
    
    	textTag_color_green = Gtk::TextTag::create();
    	textTag_color_red = Gtk::TextTag::create();
    	textTagTable = Gtk::TextTagTable::create();
    	textTagTable->add(textTag_color_green);
    	textTagTable->add(textTag_color_red);
    	textBuffer = Gtk::TextBuffer::create(textTagTable);
    	textView_log->set_buffer(textBuffer);
    	textView_log->set_cursor_visible(true);
    
    	initial_comboBox_baudrate();	//Function in Communication_Interface_Window_From.cpp File
    	initial_comboBox_byte_size();	//Function in Communication_Interface_Window_From.cpp File
    	initial_comboBox_parity();		//Function in Communication_Interface_Window_From.cpp File
    	initial_comboBox_stop_bits();	//Function in Communication_Interface_Window_From.cpp File
    	initial_comboBox_comx();		//Function in Communication_Interface_Window_From.cpp File
    
    	initial_connections();
    
    	gdkDisplay = textView_log->get_display();
    	gdkWindow = textView_log->Gtk::Widget::get_window();
    }
    

    dies ist die Funktion in der die Signal/Slot Verbindungen entstehen

    void Communication_Interface::initial_connections()
    {
    	button_update_comx->signal_clicked().connect(sigc::mem_fun(this, &Communication_Interface::initial_comboBox_comx));
    	button_open->signal_clicked().connect(sigc::mem_fun(this, &Communication_Interface::open_comx));
    	button_close->signal_clicked().connect(sigc::mem_fun(this, &Communication_Interface::close_comx));
    	button_clear_log->signal_clicked().connect(sigc::mem_fun(this, &Communication_Interface::clear_log));
    	signal_update_receive_value.connect(sigc::mem_fun(this, &Communication_Interface::update_lines));
    }
    

    in der Funktion update_lines() wird ein zweiter Thread gestartet die Funktion dafür ist weiter unten run_update_lines(). Den zweiten Thread starte ich um den ersten Thread wieder in die funktion run_receive() zurück zu kehren wegen dem Signal Emit.

    In run_update_lines() wird dann über iteratoren der vector abgefahren und die Werte aus den jeweiligen zellen rausgeholt. Zum schluss wird der Inhalt zwischen end Iterator und anfangs Iterator gelöscht.

    Inerhalb der Funktion lese ich in der while schleife alles aus und schreibe es nach textBuffer aber wärend dieser Aktion ist es nicht am Bildschirm zu sehen.
    Ich muss ständig mit der maus in den TextView bereich klicken.

    void Communication_Interface::update_lines()
    {
    	std::cout << vectorReceive->size() << std::endl;
    	Glib::Thread::create(sigc::mem_fun(this, &Communication_Interface::run_update_lines), false);
    }
    
    void Communication_Interface::run_update_lines()
    {
    	std::vector<unsigned char>::iterator iter_start, iter_start_erase, iter_end;
    	mutexVariable.lock();
    	iter_start = iter_start_erase = vectorReceive->begin();
    	iter_end = vectorReceive->end();
    	mutexVariable.unlock();
    	std::cout << vectorReceive->size() << std::endl;
    
    	if(iter_start == iter_end)
    		std::cout << "Iteratoren Equal!\n";
    
    	std::cout << (unsigned int)*(++iter_start) << " ;; " << (unsigned int)*(iter_end) << std::endl;
    
    	while(iter_start <= iter_end)
    	{
    		iter_start++;
    
    		textBuffer->insert_at_cursor(Glib::ustring::format((unsigned int)*iter_start));		
            textBuffer->insert_at_cursor("\n");
    	}
    
    	Gtk::TextIter iter_test = textBuffer->end();
    
    	mutexVariable.lock();
    	statusFlagParentThread = false;
    	mutexVariable.unlock();
    	std::cout << "ENDE--------------------------------------------------------------------\n";
    
    	vectorReceive->erase(iter_start_erase, iter_end);
    }
    

    Würde wissen wie du es mit den Childthread machst. Mir ist noch nicht ganz verständlich wie ich mein Haupthread(der die Widgets update to date hält) dazu bringen kann, wenn er seine schleife durchläuft einen Funktionsteil von mir Programmiert mit durchlaufen soll. um z.B. den Teil den ich im zweiten Thread starte im Hauptthread ausführe? Wichtig ist halt das die Oberfläche nicht einfriert.

    Danke für jede Antwort



  • Das Problem ist quasi das Auffrischen der Anzeige des TextViews nachdem du Daten in den TextBuffer geschrieben hast. Ich bin da gestern auch noch drauf gestoßen in meiner Applikation. Weiß also auch keine Lösung. An den threads, glaube ich, liegt es nicht, da ich das Auslesen der Daten und das Interpretieren und Anzeigen durch einen Ringbuffer entkoppelt habe und ich quasi ohne Signale zwischen den Threads arbeite. Child und Mainthread arbeiten bei mir unabhängig von einander.

    Hier das von mir beschriebene Problem, von gestern:

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-264493.html


Anmelden zum Antworten