'cout' im Fenster ausgeben
-
Noch_Student schrieb:
Soll ich für den Stream-Buffer eine Klasse shreiben?
Ja, genau darauf wollte ich hinaus.
-
Damit bin ich überfordert...
-
... hoffentlich nicht auch noch mit der englischen Sprache:
-
Da die Aufnahme einer neuen Zeile in einem CListBox-Objekt nur ein einfacher AddString ist, ist so ein streambuf für eine CListBox eine einfache Sache - vorausgesetzt, man weis wie das überhaupt geht. Ich habe sowas vor Urzeiten mal gemacht.
// File ListBoxStreambuf.h #pragma once // Bem.: erfordert ein #include <stdafx.h> (MFC) vor diesem include #include <streambuf> #include <iostream> #include <string> #include <vector> namespace listboxsb_detail { // -- hier werden die Zeichen-Typ-Konvertierungen erledigt template< typename E, typename Traits > int addString( CListBox& box, const std::basic_string< E, Traits >& line, E, std::basic_streambuf< E, Traits >* ) { // keine Konvertierung E == TCHAR return box.AddString( line.c_str() ); } template< typename Traits > int addString( CListBox& box, const std::basic_string< char, Traits >& line, wchar_t, std::streambuf* sb ) { // Konvertierung von char -> wchar_t std::vector< wchar_t > wstr( line.size() + 1, wchar_t(0) ); const std::ctype< wchar_t >& ctype_ = std::use_facet< std::ctype< wchar_t > >( sb->getloc() ); const char* in = line.c_str(); ctype_.widen( in, in + line.size(), &wstr[0] ); return box.AddString( &wstr[0] ); } } template< typename E, typename Traits = std::char_traits< E > > class basic_ListBoxStreambuf : public std::basic_streambuf< E, Traits > { public: explicit basic_ListBoxStreambuf( CListBox& box ) : m_box( box ) , m_line() , m_index( -1 ) {} int ListBoxIndex() const { return m_index; } protected: virtual int_type overflow( int_type m = Traits::eof() ) { if( Traits::eq_int_type( m, Traits::eof() ) ) return Traits::not_eof( m ); const E c = Traits::to_char_type( m ); if( c == E('\n') ) // LF: Zeilenende { // -- neue Zeile anhängen m_index = listboxsb_detail::addString( m_box, m_line, TCHAR(), this ); if( m_index < 0 ) return Traits::eof(); // Fehler Ausgabe ging schief m_line = std::basic_string< E, Traits >(); // m_line.clear(); } else m_line.append( 1, c ); // Zeichen für später merken return m; } private: CListBox& m_box; std::basic_string< E, Traits > m_line; int m_index; // current Index of the added line }; template< typename E, typename Traits = std::char_traits< E > > class basic_ListBoxStream : public std::basic_ostream< E, Traits > { typedef std::basic_ostream< E, Traits > base_type; public: explicit basic_ListBoxStream( CListBox& box ) : base_type( &m_sb ) , m_sb( box ) {} private: basic_ListBoxStreambuf< E, Traits > m_sb; }; typedef basic_ListBoxStreambuf< char > ListBoxStreambuf; typedef basic_ListBoxStream< char > ListBoxStream;
Damit man das bequem integrieren kann habe ich noch ein Helferlein zum Umschalten der streambufs gebastelt.
// File IosSwitch.h #pragma once #include <ios> class IosSwitch { public: IosSwitch() : m_ios(0) , m_sbMerk(0) {} IosSwitch( std::basic_ios< char >& ios, std::streambuf* sb ) : m_ios( &ios ) , m_sbMerk( ios.rdbuf( sb ) ) {} ~IosSwitch() { release(); } void set( std::basic_ios< char >& ios, std::streambuf* sb ) { release(); m_ios = &ios; m_sbMerk = ios.rdbuf( sb ); } private: void release() { if( m_ios ) { delete m_ios->rdbuf( m_sbMerk ); } } // --- Members std::basic_ios< char >* m_ios; std::streambuf* m_sbMerk; // --- nicht kopierbar IosSwitch( const IosSwitch& ); IosSwitch& operator=( const IosSwitch& ); };
Jetzt brauchst Du nur noch in Deiner View- oder Dialogklasse einen Member von IosSwitch aufzunehmen:
private: IosSwitch m_coutSwitch; // erfordert include "IosSwitch.h"
und bei der Initialisierung oder InitDialog auf den ListBoxStreambuf umschalten
BOOL DeinDialog::OnInitDialog() { CDialog::OnInitDialog(); // ... usw. m_coutSwitch.set( std::cout, new ListBoxStreambuf( m_listBox ) ); return TRUE; }
das erfordert natürlich den CListBox-Member 'm_listBox' und die includes "ListBoxStreambuf.h" und <iostream>. Anschließend landen alle Ausgaben auf cout in dieser ListBox.
Noch ein Tipp: setze das Sort-Flag der ListBox auf FALSE.jencas schrieb:
Das beschreibt das Umschalten von cout auf eine Konsole, nicht in ein MFC-Element.
Gruß
Werner
-
Werner Salomon schrieb:
jencas schrieb:
Das beschreibt das Umschalten von cout auf eine Konsole, nicht in ein MFC-Element.
Wo er Recht hat, hat er Recht. Mea culpa, mea maxima culpa
-
Hallo Werner Salomon,
alles, du vorgeschlagen hast, funktioniert gut! Super Danke!
Kannst du evtl. noch was empfehlen:
alle meine vorherige "\t" werden jetzt als Quadrate angezeigt. Kann man das irgendwie vermeiden?
Und noch eine Frage: die horizontale Bildlaufleiste erscheint nicht im ListBox, Einträge werden abgeschnitten.Übrigens kann man alles auf eine Konsole so ausgeben:
AllocConsole(); freopen("conout$", "w", stdout);
Habe selber probiert.
-
Noch_Student schrieb:
Kannst du evtl. noch was empfehlen:
alle meine vorherige "\t" werden jetzt als Quadrate angezeigt. Kann man das irgendwie vermeiden?Ja - das ist ein nicht darstellbares Steuerzeichen. Das kann man aber auch bei den Resources der ListBox einstellen: 'Use Tabstops = TRUE'.
Falls Du Sonderwünsche hast, kann man es auch selber implementieren. Füge dazu in der Klasse basic_ListBoxStreambuf ab der Zeile 59 folgenden oder einen ähnlichen Code ein:
else if( c == E('\t') ) // TAB: Tabulator { const unsigned TABSIZE = 8; // oder ein anderer Wert, je nach dem wo der Tabulator hin soll m_line.append( TABSIZE - m_line.size() % TABSIZE , ' ' ); }
so richtig 'gut' sieht das aber erst aus, wenn Du eine nicht-proportionale Schrift (z.B. Courier) als Font für die ListBox wählst.
Noch_Student schrieb:
Und noch eine Frage: die horizontale Bildlaufleiste erscheint nicht im ListBox, Einträge werden abgeschnitten.
In den Resources der ListBox 'Horizontal Scroll = TRUE' einstellen (funktioniert aber bei mir nicht!? ).
@Edit: Ah - ein MFC-Problem siehe hier. Den dort vorgeschlagenen Aufruf könnte man auch schön in den ListBoxStreambuf aufnehmen.Gruß
Werner
-
Wieder
Danke!
-
Ist alles geklärt? Ich würde den Beitrag gern in die FAQ schieben.
-
estartu schrieb:
Ist alles geklärt? Ich würde den Beitrag gern in die FAQ schieben.
Oh - welche Ehre
Bleibt vielleicht noch das Problem mit dem horizontal Scrolling in der Listbox. Ich habe mal etwas experimentiert und habe als Lösung folgende Funktion 'addString' geschrieben:
struct DC { explicit DC( CWnd& wnd ) : m_wnd( wnd ), m_dc( wnd.GetDC() ) {} ~DC() { m_wnd.ReleaseDC( m_dc ); } CDC* operator->() { return m_dc; } private: CWnd& m_wnd; CDC* m_dc; DC( const DC& ); // Kopieren verhindern DC& operator=( const DC& ); }; // -- fügt den Text 'txt' der Listbox 'box' hinzu und passt ggf. das 'horizontal extend' an, // so dass das horizontale Scrolling korrekt funktioniert. int addString( CListBox& box, const TCHAR* txt ) { const int Rand = 4; // rechter Rand in Pixeln const int ret = box.AddString( txt ); DC dc( box ); // Wrapper sorgt für CWnd::ReleaseDC (s.o.) dc->SelectObject( box.GetFont() ); // ist notwendig, im Gegensatz zum Beispiel in der Hife zu CListBox::GetHorizontalExtent! const CSize sz = dc->GetTextExtent( txt ); if( sz.cx + Rand > box.GetHorizontalExtent() ) { box.SetHorizontalExtent( sz.cx + Rand ); } return ret; }
die ist im Listing des Files ListBoxStreambuf.h ab der Zeile 12 einzufügen. Die Aufrufe der Methode CListBox::AddString in den Zeilen 17 und 28 sind entsprechend anzupassen:
return addString( box, line.c_str() ); // vorher Zeile 17
und
return addString( box, &wstr[0] ); // vorher Zeile 28
sonst ist - denke ich - wohl alles klar.
Dieser Thread über das Umleiten der Ausgabe von cout ist auch noch hilfreich.Gruß
Werner